├── .husky
├── .gitignore
└── pre-commit
├── .eslintignore
├── .prettierignore
├── netlify.toml
├── src
├── index.js
├── slices
│ ├── FaqSection
│ │ ├── preview.png
│ │ ├── meta.json
│ │ ├── index.stories.js
│ │ ├── README.md
│ │ ├── model.json
│ │ ├── index.js
│ │ └── mock.json
│ ├── AlternateGrid
│ │ ├── preview.png
│ │ ├── model.json
│ │ ├── index.stories.js
│ │ ├── index.js
│ │ └── mock.json
│ ├── CallToAction
│ │ ├── preview.png
│ │ ├── index.stories.js
│ │ ├── mock.json
│ │ ├── model.json
│ │ └── index.js
│ ├── CardsCarousel
│ │ ├── preview.png
│ │ ├── index.stories.js
│ │ ├── meta.json
│ │ ├── model.json
│ │ ├── index.js
│ │ └── mock.json
│ ├── CustomerLogos
│ │ ├── preview.png
│ │ ├── index.stories.js
│ │ ├── meta.json
│ │ ├── README.md
│ │ ├── model.json
│ │ ├── index.js
│ │ └── mock.json
│ ├── ImagesSlider
│ │ ├── preview.png
│ │ ├── index.stories.js
│ │ ├── meta.json
│ │ ├── README.md
│ │ ├── model.json
│ │ ├── mock.json
│ │ └── index.js
│ ├── PricingTable
│ │ ├── preview.png
│ │ ├── index.stories.js
│ │ ├── meta.json
│ │ ├── styles.js
│ │ ├── README.md
│ │ ├── model.json
│ │ ├── index.js
│ │ └── mock.json
│ ├── VideoHighlights
│ │ ├── preview.png
│ │ ├── meta.json
│ │ ├── styles.js
│ │ ├── index.stories.js
│ │ ├── README.md
│ │ ├── model.json
│ │ ├── index.js
│ │ └── mock.json
│ ├── TestimonialsSlider
│ │ ├── preview.png
│ │ ├── index.stories.js
│ │ ├── meta.json
│ │ ├── README.md
│ │ ├── model.json
│ │ ├── index.js
│ │ └── mock.json
│ └── index.js
├── components
│ ├── Tabs
│ │ ├── index.js
│ │ ├── index.stories.js
│ │ ├── styles.js
│ │ ├── TabsItem.js
│ │ └── Tabs.js
│ ├── Accordion
│ │ ├── index.js
│ │ ├── index.stories.js
│ │ ├── Accordion.js
│ │ ├── AccordionItem.js
│ │ └── mock.json
│ ├── Card
│ │ ├── index.js
│ │ ├── CardContent.js
│ │ ├── CardFooter.js
│ │ ├── Card.js
│ │ ├── CardImage.js
│ │ ├── mock.json
│ │ └── index.stories.js
│ ├── Head
│ │ ├── index.stories.js
│ │ └── index.js
│ ├── Wrap
│ │ ├── index.stories.js
│ │ └── index.js
│ ├── Desc
│ │ ├── index.stories.js
│ │ └── index.js
│ ├── Title
│ │ ├── index.stories.js
│ │ └── index.js
│ ├── Slice
│ │ ├── index.stories.js
│ │ └── index.js
│ ├── Eyebrow
│ │ ├── index.stories.js
│ │ └── index.js
│ ├── Slider
│ │ ├── DotsWithLabel.js
│ │ ├── index.stories.js
│ │ ├── index.js
│ │ └── styles.js
│ ├── index.js
│ ├── TextBlock
│ │ ├── index.js
│ │ └── index.stories.js
│ ├── Video
│ │ ├── index.js
│ │ └── index.stories.js
│ ├── Button
│ │ ├── index.stories.js
│ │ └── index.js
│ ├── GridLayout
│ │ ├── index.js
│ │ └── index.stories.js
│ └── Icon
│ │ ├── index.stories.js
│ │ └── index.js
├── utils
│ └── prop-types.js
└── theme.js
├── .storybook
├── preview-head.html
├── main.js
└── preview.js
├── .lintstagedrc
├── .prettierrc
├── README.md
├── sm.config.json
├── .editorconfig
├── .eslintrc.js
├── .gitignore
├── package.json
└── sm.json
/.husky/.gitignore:
--------------------------------------------------------------------------------
1 | _
2 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | !.eslintrc.js
3 | *.test.js
4 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | yarn lint-staged
5 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .cache
2 | package.json
3 | package-lock.json
4 | node_modules
5 | public
6 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | base = "/"
3 | command = "yarn run build-storybook"
4 | publish = "storybook-static"
5 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export { default as theme } from './theme'
2 | import * as Slices from './slices'
3 | export { Slices }
4 |
--------------------------------------------------------------------------------
/src/slices/FaqSection/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prismicio/essential-slices/master/src/slices/FaqSection/preview.png
--------------------------------------------------------------------------------
/src/slices/AlternateGrid/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prismicio/essential-slices/master/src/slices/AlternateGrid/preview.png
--------------------------------------------------------------------------------
/src/slices/CallToAction/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prismicio/essential-slices/master/src/slices/CallToAction/preview.png
--------------------------------------------------------------------------------
/src/slices/CardsCarousel/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prismicio/essential-slices/master/src/slices/CardsCarousel/preview.png
--------------------------------------------------------------------------------
/src/slices/CustomerLogos/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prismicio/essential-slices/master/src/slices/CustomerLogos/preview.png
--------------------------------------------------------------------------------
/src/slices/ImagesSlider/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prismicio/essential-slices/master/src/slices/ImagesSlider/preview.png
--------------------------------------------------------------------------------
/src/slices/PricingTable/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prismicio/essential-slices/master/src/slices/PricingTable/preview.png
--------------------------------------------------------------------------------
/src/slices/VideoHighlights/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prismicio/essential-slices/master/src/slices/VideoHighlights/preview.png
--------------------------------------------------------------------------------
/.storybook/preview-head.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/slices/TestimonialsSlider/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prismicio/essential-slices/master/src/slices/TestimonialsSlider/preview.png
--------------------------------------------------------------------------------
/.lintstagedrc:
--------------------------------------------------------------------------------
1 | {
2 | '*.{js,jsx}': [
3 | 'eslint . --fix'
4 | ],
5 | '**/*.{js, jsx, json,md}': [
6 | 'prettier --write'
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/src/components/Tabs/index.js:
--------------------------------------------------------------------------------
1 | import Tabs from './Tabs'
2 | import TabsItem from './TabsItem'
3 |
4 | Tabs.Item = TabsItem
5 |
6 | export default Tabs
7 |
--------------------------------------------------------------------------------
/src/components/Accordion/index.js:
--------------------------------------------------------------------------------
1 | import Accordion from './Accordion'
2 | import AccordionItem from './AccordionItem'
3 |
4 | Accordion.Item = AccordionItem
5 |
6 | export default Accordion
7 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "endOfLine": "lf",
3 | "arrowParens": "always",
4 | "semi": false,
5 | "singleQuote": true,
6 | "tabWidth": 2,
7 | "trailingComma": "es5",
8 | "bracketSpacing": true
9 | }
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | React essential slices
2 |
3 | Storybook automatically deployed to Netlify:
4 | https://react-essential-slices.netlify.app
5 |
6 | Run `yarn build` to bundle the slices library (using `microbundle`) and create
7 | the library manifest (`sm.json` file)
8 |
--------------------------------------------------------------------------------
/sm.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "libraryName": "React essentials",
3 | "framework": "next",
4 | "gitUrl": "//github.com/prismicio/essential-slices",
5 | "pathToLibrary": "src",
6 | "dependencies": ["theme-ui"],
7 | "css": [],
8 | "devDependencies": []
9 | }
10 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/src/slices/ImagesSlider/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ImagesSlider from '.'
3 | import mock from './mock.json'
4 |
5 | export default {
6 | title: 'Slices/ImagesSlider',
7 | component: ImagesSlider,
8 | }
9 | export const Default = () =>
10 |
--------------------------------------------------------------------------------
/src/components/Card/index.js:
--------------------------------------------------------------------------------
1 | import Card from './Card'
2 | import CardContent from './CardContent'
3 | import CardFooter from './CardFooter'
4 | import CardImage from './CardImage'
5 |
6 | Card.Content = CardContent
7 | Card.Footer = CardFooter
8 | Card.Image = CardImage
9 |
10 | export default Card
11 |
--------------------------------------------------------------------------------
/src/slices/CardsCarousel/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import CardsCarousel from '.'
3 | import mock from './mock.json'
4 |
5 | export default {
6 | title: 'Slices/CardsCarousel',
7 | component: CardsCarousel,
8 | }
9 | export const Default = () =>
10 |
--------------------------------------------------------------------------------
/src/slices/PricingTable/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PricingTable from '.'
3 | import mock from './mock.json'
4 |
5 | export default {
6 | title: 'Slices/PricingTable',
7 | component: PricingTable,
8 | }
9 |
10 | export const Default = () =>
11 |
--------------------------------------------------------------------------------
/src/slices/CustomerLogos/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import CustomerLogos from '.'
3 | import mock from './mock.json'
4 |
5 | export default {
6 | title: 'Slices/CustomerLogos',
7 | component: CustomerLogos,
8 | }
9 |
10 | export const Default = () =>
11 |
--------------------------------------------------------------------------------
/src/slices/TestimonialsSlider/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import TestimonialsSlider from '.'
3 | import mock from './mock.json'
4 |
5 | export default {
6 | title: 'Slices/TestimonialsSlider',
7 | component: TestimonialsSlider,
8 | }
9 | export const Default = () =>
10 |
--------------------------------------------------------------------------------
/src/slices/CardsCarousel/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Cards Carousel Section",
3 | "description": "A Carousel section by Octahedroid",
4 | "contributors": ["https://github.com/octahedroid/"],
5 | "sandboxUrl": "",
6 | "components": [],
7 | "tags": ["Banner", "Call to action"],
8 | "collection": "essentials ",
9 | "dependencies": []
10 | }
11 |
--------------------------------------------------------------------------------
/src/slices/ImagesSlider/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Cards Carousel Section",
3 | "description": "A Carousel section by Octahedroid",
4 | "contributors": ["https://github.com/octahedroid/"],
5 | "sandboxUrl": "",
6 | "components": [],
7 | "tags": ["Banner", "Call to action"],
8 | "collection": "essentials ",
9 | "dependencies": []
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/Head/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Head from '.'
3 |
4 | export default {
5 | title: 'Components/Head',
6 | component: Head,
7 | }
8 |
9 | export const Default = () => (
10 |
Content
11 | )
12 |
13 | Default.story = {
14 | name: 'Default',
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/Wrap/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Wrap from '.'
3 |
4 | export default {
5 | title: 'Components/Wrap',
6 | component: Wrap,
7 | }
8 |
9 | export const Default = () => (
10 | Content
11 | )
12 |
13 | Default.story = {
14 | name: 'Default',
15 | }
16 |
--------------------------------------------------------------------------------
/src/slices/CustomerLogos/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Customer Logos Section",
3 | "description": "A Customer Logos section by Octahedroid",
4 | "contributors": ["https://github.com/octahedroid/"],
5 | "sandboxUrl": "",
6 | "components": [],
7 | "tags": ["Banner", "Call to action"],
8 | "collection": "essentials ",
9 | "dependencies": []
10 | }
11 |
--------------------------------------------------------------------------------
/src/slices/PricingTable/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Pricing Table Section",
3 | "description": "A Pricing Table section by Octahedroid",
4 | "contributors": ["https://github.com/octahedroid/"],
5 | "sandboxUrl": "",
6 | "components": [],
7 | "tags": ["Banner", "Call to action"],
8 | "collection": "essentials ",
9 | "dependencies": []
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/Desc/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Desc from '.'
3 |
4 | export default {
5 | title: 'Components/Desc',
6 | component: Desc,
7 | }
8 |
9 | export const Default = () => (
10 | Content
11 | )
12 |
13 | Default.story = {
14 | name: 'Default',
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/Card/CardContent.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 |
5 | const CardContent = ({ children, ...props }) => {
6 | return {children}
7 | }
8 |
9 | CardContent.propTypes = {
10 | children: PropTypes.node.isRequired,
11 | }
12 |
13 | export default CardContent
14 |
--------------------------------------------------------------------------------
/src/slices/TestimonialsSlider/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Testimonials Slider Section",
3 | "description": "A Call to action section by Octahedroid",
4 | "contributors": ["https://github.com/octahedroid/"],
5 | "sandboxUrl": "",
6 | "components": [],
7 | "tags": ["Banner", "Slider", "Testimonial"],
8 | "collection": "essentials ",
9 | "dependencies": []
10 | }
11 |
--------------------------------------------------------------------------------
/src/slices/VideoHighlights/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Video Highlights",
3 | "description": "A list of video elements",
4 | "imageUrl": "",
5 | "contributors": ["https://github.com/sarasoueidan/"],
6 | "sandboxUrl": "",
7 | "components": [],
8 | "tags": ["Essentials", "Videos", "Slider"],
9 | "collection": "essentials",
10 | "dependencies": []
11 | }
12 |
--------------------------------------------------------------------------------
/src/components/Title/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Title from '.'
3 |
4 | export default {
5 | title: 'Components/Title',
6 | component: Title,
7 | }
8 |
9 | export const Default = () => (
10 |
11 | Title text
12 |
13 | )
14 |
15 | Default.story = {
16 | name: 'Default',
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/Desc/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 |
5 | const Desc = ({ children, ...props }) => {
6 | return (
7 |
8 | {children}
9 |
10 | )
11 | }
12 |
13 | Desc.propTypes = {
14 | children: PropTypes.node.isRequired,
15 | }
16 |
17 | export default Desc
18 |
--------------------------------------------------------------------------------
/src/slices/FaqSection/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "FAQ section",
3 | "description": "Display an accordion FAQ, with optional image",
4 | "contributors": [
5 | "https://github.com/SaraSoueidan",
6 | "https://github.com/hypervillain/"
7 | ],
8 | "sandboxUrl": "",
9 | "components": ["ps-accordion"],
10 | "tags": ["Essentials", "Landing pages"],
11 | "collection": "prismic-essentials",
12 | "dependencies": []
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/Card/CardFooter.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 |
5 | const CardFooter = ({ children, ...props }) => {
6 | return (
7 |
8 | {children}
9 |
10 | )
11 | }
12 |
13 | CardFooter.propTypes = {
14 | children: PropTypes.node.isRequired,
15 | }
16 |
17 | export default CardFooter
18 |
--------------------------------------------------------------------------------
/.storybook/main.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | stories: [
3 | '../src/slices/**/*.stories.js',
4 | '../src/components/**/*.stories.js',
5 | '../src/**/*.stories.mdx',
6 | ],
7 | addons: [
8 | '@storybook/addon-docs',
9 | '@storybook/addon-viewport/register',
10 | '@storybook/addon-a11y/register',
11 | '@storybook/addon-actions',
12 | '@storybook/addon-links',
13 | 'storybook-addon-color-mode/register',
14 | ],
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/Slice/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Box } from 'theme-ui'
3 | import Slice from '.'
4 |
5 | export default {
6 | title: 'Components/Slice',
7 | component: Slice,
8 | }
9 |
10 | export const Default = () => (
11 |
12 | Content
13 |
14 | )
15 |
16 | Default.story = {
17 | name: 'Default',
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/Tabs/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Tabs from '.'
3 |
4 | export default {
5 | title: 'Components/Tabs',
6 | component: Tabs,
7 | }
8 |
9 | export const Default = () => (
10 |
11 | Content1
12 | Content2
13 | Content3
14 |
15 | )
16 |
17 | Default.story = {
18 | name: 'Default',
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/Eyebrow/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Box } from 'theme-ui'
3 | import Eyebrow from '.'
4 |
5 | export default {
6 | title: 'Components/Eyebrow',
7 | component: Eyebrow,
8 | }
9 |
10 | export const Default = () => (
11 |
19 | Title text
20 |
21 | )
22 |
--------------------------------------------------------------------------------
/src/slices/CallToAction/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import CallToAction from '.'
3 | import mock from './mock.json'
4 |
5 | export default {
6 | title: 'Slices/CallToAction',
7 | component: CallToAction,
8 | }
9 |
10 | function linkResolver(doc) {
11 | return `/link/to/${doc.uid}`
12 | }
13 |
14 | export const Simple = () => (
15 |
16 | )
17 |
18 | Simple.story = {
19 | name: 'Simple Example',
20 | }
21 |
--------------------------------------------------------------------------------
/src/slices/index.js:
--------------------------------------------------------------------------------
1 | export { default as AlternateGrid } from './AlternateGrid'
2 | export { default as CallToAction } from './CallToAction'
3 | export { default as CardsCarousel } from './CardsCarousel'
4 | export { default as CustomerLogos } from './CustomerLogos'
5 | export { default as FaqSection } from './FaqSection'
6 | export { default as ImagesSlider } from './ImagesSlider'
7 | export { default as PricingTable } from './PricingTable'
8 | export { default as VideoHighlights } from './VideoHighlights'
9 |
--------------------------------------------------------------------------------
/src/components/Head/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 |
5 | const Head = ({ children, ...props }) => {
6 | return (
7 |
14 | {children}
15 |
16 | )
17 | }
18 |
19 | Head.propTypes = {
20 | children: PropTypes.node.isRequired,
21 | }
22 |
23 | export default Head
24 |
--------------------------------------------------------------------------------
/src/components/Slider/DotsWithLabel.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Box } from 'theme-ui'
3 | import { dotStyles, dotLabelStyles } from './styles'
4 |
5 | const DotsWithLabel = (index, dotsLabel) => {
6 | return (
7 |
13 | {`${dotsLabel} ${index + 1}`}
14 |
15 | )
16 | }
17 |
18 | export default DotsWithLabel
19 |
--------------------------------------------------------------------------------
/src/utils/prop-types.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 |
3 | export const structuredTextPropTypes = PropTypes.arrayOf(
4 | PropTypes.shape({
5 | type: PropTypes.string,
6 | text: PropTypes.string,
7 | spans: PropTypes.array,
8 | })
9 | )
10 |
11 | export const imagePropTypes = PropTypes.shape({
12 | dimensions: PropTypes.shape({
13 | width: PropTypes.number,
14 | height: PropTypes.number,
15 | }),
16 | alt: PropTypes.string,
17 | copyright: PropTypes.string,
18 | url: PropTypes.string,
19 | })
20 |
--------------------------------------------------------------------------------
/src/components/Wrap/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 |
5 | const Wrap = ({ children, ...props }) => {
6 | return (
7 |
16 | {children}
17 |
18 | )
19 | }
20 |
21 | Wrap.propTypes = {
22 | children: PropTypes.node.isRequired,
23 | }
24 |
25 | export default Wrap
26 |
--------------------------------------------------------------------------------
/src/components/Title/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 |
5 | const Title = ({ children, ...props }) => {
6 | return (
7 |
16 | {children}
17 |
18 | )
19 | }
20 |
21 | Title.propTypes = {
22 | children: PropTypes.node.isRequired,
23 | }
24 |
25 | export default Title
26 |
--------------------------------------------------------------------------------
/src/components/Card/Card.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 |
5 | const Card = ({ children, ...props }) => {
6 | return (
7 |
18 | {children}
19 |
20 | )
21 | }
22 |
23 | Card.propTypes = {
24 | children: PropTypes.node.isRequired,
25 | }
26 |
27 | export default Card
28 |
--------------------------------------------------------------------------------
/src/components/Card/CardImage.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Box } from 'theme-ui'
3 |
4 | import { imagePropTypes } from '../../utils/prop-types'
5 |
6 | const CardImage = ({ image, ...props }) => {
7 | const { url, alt, dimensions } = image
8 | return (
9 |
17 | )
18 | }
19 |
20 | CardImage.propTypes = {
21 | image: imagePropTypes.isRequired,
22 | }
23 |
24 | export default CardImage
25 |
--------------------------------------------------------------------------------
/src/components/Accordion/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Accordion from '.'
3 | import mock from './mock.json'
4 |
5 | export default {
6 | title: 'components/Accordion',
7 | component: Accordion,
8 | }
9 |
10 | export const Default = () => {
11 | return (
12 |
13 | {mock.items.map(({ text, title }) => {
14 | return (
15 |
22 | )
23 | })}
24 |
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/Eyebrow/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 |
5 | const Eyebrow = ({ children, ...props }) => {
6 | return (
7 |
18 | {children}
19 |
20 | )
21 | }
22 |
23 | Eyebrow.propTypes = {
24 | children: PropTypes.node.isRequired,
25 | }
26 |
27 | export default Eyebrow
28 |
--------------------------------------------------------------------------------
/src/components/Slice/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 |
5 | const Slice = ({ children, ...props }) => {
6 | return (
7 |
19 | {children}
20 |
21 | )
22 | }
23 |
24 | Slice.propTypes = {
25 | children: PropTypes.node.isRequired,
26 | }
27 |
28 | export default Slice
29 |
--------------------------------------------------------------------------------
/src/slices/VideoHighlights/styles.js:
--------------------------------------------------------------------------------
1 | import { Icon } from '../../components'
2 |
3 | const tabItem = {
4 | marginBottom: 0,
5 | px: 'small',
6 | py: 'xsmall',
7 | display: 'block',
8 | width: 'inherit',
9 | textAlign: 'left',
10 | textOverflow: 'ellipsis',
11 | whiteSpace: 'nowrap',
12 | overflow: 'hidden',
13 | backgroundSize: '1em 1em',
14 | backgroundRepeat: 'no-repeat',
15 | backgroundPosition: 'right center',
16 | backgroundImage: `url('${Icon.encode('play')}')`,
17 | ':hover': {
18 | textDecoration: 'underline',
19 | },
20 | '&.active': {
21 | backgroundImage: `url('${Icon.encode('playBlack')}')`,
22 | fontWeight: 'bold',
23 | },
24 | }
25 |
26 | export default tabItem
27 |
--------------------------------------------------------------------------------
/src/components/Accordion/Accordion.js:
--------------------------------------------------------------------------------
1 | import React, { Children, cloneElement, useState } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 |
5 | const Accordion = ({ children }) => {
6 | const [active, setActive] = useState()
7 |
8 | const items = Children.map(children, (item, index) => {
9 | if (!item) return undefined
10 |
11 | const isActive = index === active
12 |
13 | return cloneElement(item, {
14 | active: isActive,
15 | onClick: () => setActive(index === active ? null : index),
16 | })
17 | })
18 | return {items}
19 | }
20 |
21 | Accordion.propTypes = {
22 | children: PropTypes.node.isRequired,
23 | }
24 |
25 | export default Accordion
26 |
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | export { default as Button } from './Button'
2 | export { default as Card } from './Card'
3 | export { default as Desc } from './Desc'
4 | export { default as Eyebrow } from './Eyebrow'
5 | export { default as Grid } from './GridLayout'
6 | export { default as Head } from './Head'
7 | export { default as Slice } from './Slice'
8 | export { default as Slider } from './Slider'
9 | export { default as Title } from './Title'
10 | export { default as Wrap } from './Wrap'
11 | export { default as Icon } from './Icon'
12 | export { default as Accordion } from './Accordion'
13 | export { default as TextBlock } from './TextBlock'
14 | export { default as Video } from './Video'
15 | export { default as Tabs } from './Tabs'
16 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | globals: {
3 | __PATH_PREFIX__: true,
4 | },
5 | env: {
6 | browser: true,
7 | es6: true,
8 | node: true,
9 | },
10 | settings: {
11 | react: {
12 | pragma: 'React',
13 | version: 'detect',
14 | },
15 | },
16 | extends: ['airbnb', 'plugin:prettier/recommended'],
17 | parserOptions: {
18 | ecmaVersion: 2018,
19 | sourceType: 'module',
20 | },
21 | rules: {
22 | camelcase: [2, { properties: 'never' }],
23 | 'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx'] }],
24 | 'import/no-cycle': 0,
25 | 'no-underscore-dangle': 0,
26 | 'jsx-a11y/anchor-is-valid': 0,
27 | 'react/jsx-props-no-spreading': 'off',
28 | },
29 | }
30 |
--------------------------------------------------------------------------------
/src/slices/VideoHighlights/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ThemeProvider } from 'theme-ui'
3 | import theme from '../../theme'
4 | import VideoHighlights from '.'
5 | import mock from './mock.json'
6 |
7 | export default {
8 | title: 'Slices/VideoHighlights',
9 | component: VideoHighlights,
10 | }
11 |
12 | export const Default = () =>
13 |
14 | export const WithCustomTheme = () => {
15 | const customTheme = {
16 | ...theme,
17 | container: {
18 | ...theme.container,
19 | slice: {
20 | color: '#FFF',
21 | background: 'rgb(112, 99, 255)',
22 | border: '8px solid pink',
23 | },
24 | title: {
25 | textAlign: 'right',
26 | },
27 | description: {
28 | mr: 0,
29 | textAlign: 'right',
30 | },
31 | },
32 | }
33 | return (
34 |
35 |
36 |
37 | )
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/Tabs/styles.js:
--------------------------------------------------------------------------------
1 | export const baseTabsStyles = {}
2 |
3 | // Tabs styles.
4 | export const getTabsHeaderStyles = (hideDivider) => {
5 | const dividerStyles = !hideDivider
6 | ? { borderBottom: '1px solid', borderColor: 'border' }
7 | : {}
8 |
9 | return {
10 | display: 'flex',
11 | mb: 'small',
12 | ...dividerStyles,
13 | }
14 | }
15 |
16 | // Tabs Item styles.
17 | export const getBaseTabsItemStyles = (disabled) => {
18 | const disabledStyles = disabled ? { opacity: 0.5, cursor: 'not-allowed' } : {}
19 |
20 | return {
21 | alignItems: 'center',
22 | display: 'flex',
23 | cursor: 'pointer',
24 | fontSize: 'small',
25 | mx: 'xsmall',
26 | position: 'relative',
27 | py: 'xsmall',
28 | outline: 'none',
29 | ...disabledStyles,
30 | '&:first-of-type': {
31 | ml: 0,
32 | },
33 | '&.active': {
34 | fontWeight: 'bold',
35 | outline: '3px solid currentColor',
36 | outlineOffset: '5px',
37 | },
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/TextBlock/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { RichText } from 'prismic-reactjs'
4 | import { Box } from 'theme-ui'
5 | import { structuredTextPropTypes } from '../../utils/prop-types'
6 |
7 | const TextBlock = ({ title, description, icon, ...props }) => {
8 | return (
9 |
15 | {icon && }
16 |
17 |
18 |
19 | )
20 | }
21 |
22 | TextBlock.propTypes = {
23 | title: structuredTextPropTypes,
24 | description: structuredTextPropTypes,
25 | icon: PropTypes.shape({
26 | url: PropTypes.string,
27 | }),
28 | }
29 |
30 | TextBlock.defaultProps = {
31 | title: '',
32 | description: '',
33 | icon: null,
34 | }
35 |
36 | export default TextBlock
37 |
--------------------------------------------------------------------------------
/src/components/Video/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 |
5 | const Video = ({ source, ...props }) => {
6 | const { html } = source
7 | return (
8 |
9 |
27 |
32 |
33 |
34 | )
35 | }
36 |
37 | Video.propTypes = {
38 | source: PropTypes.shape({
39 | html: PropTypes.string,
40 | }).isRequired,
41 | }
42 |
43 | export default Video
44 |
--------------------------------------------------------------------------------
/src/components/Button/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Button from '.'
3 |
4 | export default {
5 | title: 'Components/Button',
6 | component: Button,
7 | }
8 |
9 | export const Default = () =>
10 |
11 | export const Link = () => {
12 | const mock = {
13 | button_label: 'Click Here',
14 | button_link: {
15 | link_type: 'Web',
16 | url: '/dashboard',
17 | },
18 | }
19 |
20 | return (
21 |
26 | )
27 | }
28 |
29 | export const LinkResolver = () => {
30 | const linkResolver = (doc) => `/link/to/${doc.uid}`
31 | const mock = {
32 | button_label: 'Click Here',
33 | button_link: {
34 | link_type: 'Document',
35 | uid: 9,
36 | },
37 | }
38 |
39 | return (
40 |
45 | )
46 | }
47 |
--------------------------------------------------------------------------------
/src/slices/CallToAction/mock.json:
--------------------------------------------------------------------------------
1 | {
2 | "slice_type": "call_to_action",
3 | "slice_label": "",
4 | "items": [{}],
5 | "primary": {
6 | "icon_image": {
7 | "dimensions": {
8 | "width": 72,
9 | "height": 72
10 | },
11 | "alt": "Some alt text for the image.",
12 | "copyright": null,
13 | "url": "https://images.prismic.io/slicesexamples/0a64ab37-8ba3-4882-a766-53bad869d3cf_cta-eyebrow-icon.svg?auto=compress,format&w=900"
14 | },
15 | "title": [
16 | {
17 | "type": "heading1",
18 | "text": "Collector Slices kit",
19 | "spans": []
20 | }
21 | ],
22 | "paragraph": [
23 | {
24 | "type": "paragraph",
25 | "text": "It’s very easy to create stylish and beautiful prototypes for your future projects, both graphical and dynamic.",
26 | "spans": []
27 | }
28 | ],
29 | "button_link": {
30 | "link_type": "Web",
31 | "url": "https://prismic.io"
32 | },
33 | "button_label": "Click Here"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/Video/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Video from '.'
3 |
4 | export default {
5 | title: 'Components/Video',
6 | component: Video,
7 | }
8 |
9 | const mock = {
10 | video_src: {
11 | type: 'video',
12 | thumbnail_height: 360,
13 | provider_url: 'https://www.youtube.com/',
14 | thumbnail_url: 'https://i.ytimg.com/vi/gYm9Q6J5gp8/hqdefault.jpg',
15 | html:
16 | '',
17 | provider_name: 'YouTube',
18 | version: '1.0',
19 | title: 'Single Page Apps - Fat frontend or fat backend, not both',
20 | author_name: 'Prismic',
21 | height: 270,
22 | author_url: 'https://www.youtube.com/channel/UCJq6AEgtWeZt7ziQ-fLKOeA',
23 | thumbnail_width: 480,
24 | width: 480,
25 | embed_url: 'https://www.youtube.com/watch?v=gYm9Q6J5gp8',
26 | },
27 | }
28 |
29 | export const Default = () =>
30 | Default.story = {
31 | name: 'Default',
32 | }
33 |
--------------------------------------------------------------------------------
/src/slices/PricingTable/styles.js:
--------------------------------------------------------------------------------
1 | import { Icon } from '../../components'
2 |
3 | export const table = {
4 | listStyle: 'none',
5 | padding: 0,
6 | margin: 0,
7 | display: 'grid',
8 | gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
9 | gridColumnGap: 'vMargin',
10 | }
11 |
12 | export const tableOption = {
13 | bg: 'secondary',
14 | borderRadius: 'card',
15 | mb: 'vSpace',
16 | p: 'hPadding',
17 | '@media all and (min-width: 40em)': {
18 | '&:nth-of-type(2n)': {
19 | bg: 'white',
20 | border: '1px solid',
21 | },
22 | },
23 | }
24 |
25 | export const features = {
26 | mt: 'hPadding',
27 | ul: {
28 | pl: '0',
29 | listStyleType: 'none',
30 | },
31 | li: {
32 | mb: '1em',
33 | display: 'flex',
34 | alignItems: 'center',
35 | },
36 | 'li:before': {
37 | content: `url('${Icon.encode('feature')}')`,
38 | mr: '25px',
39 | width: '1em',
40 | height: '1em',
41 | color: 'primary',
42 | },
43 | 'li.not-included:before': {
44 | content: `url('${Icon.encode('notIncluded')}')`,
45 | color: 'textGrey',
46 | },
47 | '.feature-icon': {
48 | color: 'primary',
49 | },
50 | }
51 |
--------------------------------------------------------------------------------
/src/components/TextBlock/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import TextBlock from '.'
3 |
4 | export default {
5 | title: 'Components/TextBlock',
6 | component: TextBlock,
7 | }
8 |
9 | const mock = {
10 | optional_icon: {
11 | dimensions: {
12 | width: 2048,
13 | height: 1536,
14 | },
15 | alt: null,
16 | copyright: null,
17 | url:
18 | 'https://images.prismic.io/repoz/0b72eb6f-1824-4891-914a-c43fdee5b893_running.png?auto=compress,format',
19 | },
20 | title: [
21 | {
22 | type: 'heading3',
23 | text: 'Integrate with the SliceZone',
24 | spans: [],
25 | },
26 | ],
27 | description: [
28 | {
29 | type: 'paragraph',
30 | text:
31 | 'This component has been matched by the SliceZone. Its model has been fetched from vue-essential-slices.',
32 | spans: [],
33 | },
34 | ],
35 | }
36 |
37 | export const Default = () => (
38 |
39 | )
40 |
41 | export const WithIcon = () => (
42 |
47 | )
48 |
--------------------------------------------------------------------------------
/src/components/Tabs/TabsItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 |
5 | import { getBaseTabsItemStyles } from './styles'
6 |
7 | const TabsItem = ({ active, disabled, label, variant, onActivate }) => {
8 | const baseTabsItemStyles = getBaseTabsItemStyles(disabled)
9 |
10 | const tabsItemStyles = {
11 | ...baseTabsItemStyles,
12 | }
13 |
14 | const onClickHandler = () => {
15 | if (onActivate) onActivate()
16 | }
17 |
18 | const defaultProps = !disabled ? { onClick: onClickHandler } : {}
19 | const className = active ? 'active' : undefined
20 |
21 | return (
22 |
29 | {label}
30 |
31 | )
32 | }
33 |
34 | TabsItem.defaultProps = {
35 | active: false,
36 | disabled: false,
37 | variant: 'default',
38 | onActivate: undefined,
39 | }
40 |
41 | TabsItem.propTypes = {
42 | active: PropTypes.bool,
43 | disabled: PropTypes.bool,
44 | variant: PropTypes.string,
45 | label: PropTypes.string.isRequired,
46 | onActivate: PropTypes.func,
47 | }
48 |
49 | export default TabsItem
50 |
--------------------------------------------------------------------------------
/src/slices/FaqSection/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ThemeProvider } from 'theme-ui'
3 | import theme from '../../theme'
4 | import mock from './mock.json'
5 | import FaqSection from '.'
6 |
7 | export default {
8 | title: 'Slices/FaqSection',
9 | component: FaqSection,
10 | }
11 |
12 | export const Default = () => {
13 | return
14 | }
15 |
16 | export const WithoutImage = () => {
17 | return (
18 |
27 | )
28 | }
29 |
30 | export const WithCustomTheme = () => {
31 | const customTheme = {
32 | ...theme,
33 | container: {
34 | ...theme.container,
35 | wrapper: {
36 | transform: 'rotateZ(2deg)',
37 | },
38 | eyebrow: {
39 | color: 'tomato',
40 | },
41 | },
42 | }
43 | return (
44 |
45 |
54 |
55 | )
56 | }
57 |
--------------------------------------------------------------------------------
/src/slices/CallToAction/model.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "Slice",
3 | "fieldset": "Call to action",
4 | "description": "A predesigned 'Call to action' for section for your site",
5 | "icon": "notifications_active",
6 | "display": "list",
7 | "non-repeat": {
8 | "icon_image": {
9 | "type": "Image",
10 | "config": {
11 | "constraint": {},
12 | "thumbnails": [],
13 | "label": "Icon Image"
14 | }
15 | },
16 | "title": {
17 | "type": "StructuredText",
18 | "config": {
19 | "single": "heading1, heading2, heading3, heading4, heading5, heading6",
20 | "label": "Title"
21 | }
22 | },
23 | "paragraph": {
24 | "type": "StructuredText",
25 | "config": {
26 | "multi": "paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item",
27 | "label": "Paragraph"
28 | }
29 | },
30 | "button_link": {
31 | "type": "Link",
32 | "config": {
33 | "label": "Button Link"
34 | }
35 | },
36 | "button_label": {
37 | "type": "Text",
38 | "config": {
39 | "label": "Button Label",
40 | "placeholder": "Text for button"
41 | }
42 | }
43 | },
44 | "repeat": {}
45 | }
46 |
--------------------------------------------------------------------------------
/src/slices/CustomerLogos/README.md:
--------------------------------------------------------------------------------
1 | # Customer Logos Section
2 |
3 | ### Purpose of the component
4 |
5 | This component allows you to easily create a beautifully styled responsive section for showcasing companies and customers that you've worked with.
6 |
7 | ### Variations
8 |
9 | 1. Default
10 | This default version on the component is the only variation.
11 |
12 | ### Properties
13 |
14 | ```
15 | | Property | Type | Repeatable | Description | Required | Default |
16 | | ------------------ | -------------------- | -----------| -------------------------------------- | -------- | ---------------- |
17 | | eyebrow_headline | RichText \|\| String | false | A short headline for the slice | true | -- |
18 | | link | String \|\| URL | true | Url that will be used to redirect user | false | https://test.com |
19 | | logo | Key Text \|\| String | true | A image field for company logo | true | -- |
20 | | call_to_action | Key Text \|\| String | false | A label for the CTA | true | -- |
21 | | call_to_action_link| String \|\| URL | false | Call to action link | false | https://test.com |
22 | ```
23 |
--------------------------------------------------------------------------------
/src/slices/ImagesSlider/README.md:
--------------------------------------------------------------------------------
1 | # Call to action Section
2 |
3 | ### Purpose of the component
4 |
5 | This component allows you to easily create a beautifully styled responsive call to action section. Install it in your project, add the model in your custom type and fill in the content in the document and your are ready to go.
6 |
7 | ### Variations
8 |
9 | 1. Default
10 | This default version on the component is the only variation.
11 |
12 | ### Properties
13 |
14 | ```
15 | | Property | Type | Repeatable | Description | Required | Default |
16 | | ----------- | -------------------- | -----------| -------------------------------------- | -------- | ---------------- |
17 | | icon_image | Image \|\| URL | false | An Icon image for the feature | true | -- |
18 | | title | RichText \|\| String | false | A short title | true | -- |
19 | | paragraph | RichText \|\| String | false | A short paragraph | true | -- |
20 | | button_label| Key Text \|\| String | false | A label for the button input | true | -- |
21 | | button_link | String \|\| URL | false | Url that will be used to redirect user | false | https://test.com |
22 | ```
23 |
--------------------------------------------------------------------------------
/src/slices/VideoHighlights/README.md:
--------------------------------------------------------------------------------
1 | # Video Highlights Section
2 |
3 | ### Purpose of the component
4 |
5 | This component allows you to easily add a list of videos, great for showcasing playlists or video courses.
6 |
7 | ### Variations
8 |
9 | 1. Default
10 | This default version on the component is the only variation.
11 | 1. Dark
12 | This variation allows you to enable a dark mode for the section
13 |
14 | ### Properties
15 |
16 | ```
17 | | Property | Type | Repeatable | Description | Required | Default |
18 | | ------------------ | -------------------- | -----------| -------------------------------------- | -------- | ---------------- |
19 | | eyebrow_headline | RichText \|\| String | false | A short headline for the slice | true | -- |
20 | | title | RichText \|\| String | false | A title for the slice | true | -- |
21 | | description | RichText \|\| String | false | A paragraph for the slice | true | -- |
22 | | title | RichText \|\| String | true | A title to select the video | true | -- |
23 | | src.embed_url | String \|\| URL | true | The link to your video | true | -- |
24 | ```
25 |
--------------------------------------------------------------------------------
/src/slices/CustomerLogos/model.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "Slice",
3 | "fieldset": "Customer logos",
4 | "description": "Display a list of your customers logos",
5 | "icon": "person",
6 | "display": "list",
7 | "non-repeat": {
8 | "eyebrow_headline": {
9 | "type": "StructuredText",
10 | "config": {
11 | "single": "heading2",
12 | "label": "Eyebrow Headline",
13 | "placeholder": "Trusted by"
14 | }
15 | },
16 | "call_to_action": {
17 | "type": "StructuredText",
18 | "config": {
19 | "single": "paragraph",
20 | "label": "Call To Action",
21 | "placeholder": "View customer stories"
22 | }
23 | },
24 | "call_to_action_link": {
25 | "type": "Link",
26 | "config": {
27 | "allowTargetBlank": true,
28 | "label": "Call to Action Link",
29 | "placeholder": "Could be a signup link, or a link to customer stories"
30 | }
31 | }
32 | },
33 | "repeat": {
34 | "logo": {
35 | "type": "Image",
36 | "config": {
37 | "constraint": {},
38 | "thumbnails": [],
39 | "label": "Logo"
40 | }
41 | },
42 | "link": {
43 | "type": "Link",
44 | "config": {
45 | "allowTargetBlank": true,
46 | "label": "link",
47 | "placeholder": "Could be a link to use case, press article, signup..."
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/slices/VideoHighlights/model.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "Slice",
3 | "fieldset": "Video Highlights",
4 | "description": "Highlights of your video channel",
5 | "icon": "ondemand_video",
6 | "display": "list",
7 | "non-repeat": {
8 | "eyebrow_headline": {
9 | "type": "StructuredText",
10 | "config": {
11 | "single": "paragraph",
12 | "label": "Eyebrow headline",
13 | "placeholder": "Reinforce your copy with a key-worded text, to be displayed before title"
14 | }
15 | },
16 | "title": {
17 | "type": "StructuredText",
18 | "config": {
19 | "single": "heading2",
20 | "label": "Title",
21 | "placeholder": "Video Highlights"
22 | }
23 | },
24 | "description": {
25 | "type": "StructuredText",
26 | "config": {
27 | "multi": "paragraph, strong, em, hyperlink",
28 | "allowTargetBlank": true,
29 | "label": "Description",
30 | "placeholder": "These are some awesome videos ..."
31 | }
32 | }
33 | },
34 | "repeat": {
35 | "video_title": {
36 | "type": "StructuredText",
37 | "config": {
38 | "multi": "paragraph, strong",
39 | "label": "Video title",
40 | "placeholder": "My awesome video"
41 | }
42 | },
43 | "video_src": {
44 | "type": "Embed",
45 | "config": {
46 | "label": "Video src"
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { action } from '@storybook/addon-actions'
3 | import { addDecorator, addParameters } from '@storybook/react'
4 | import { ThemeProvider, BaseStyles } from 'theme-ui'
5 | import { DocsPage, DocsContainer } from '@storybook/addon-docs/blocks'
6 | import { withThemeProvider } from 'storybook-addon-color-mode'
7 |
8 | import theme from '../src/theme'
9 |
10 | // Gatsby's Link overrides:
11 | // Gatsby defines a global called ___loader to prevent its method calls from creating console errors you override it here
12 | global.___loader = {
13 | enqueue: () => {},
14 | hovering: () => {},
15 | }
16 | // Gatsby internal mocking to prevent unnecessary errors in storybook testing environment
17 | global.__PATH_PREFIX__ = ''
18 | // This is to utilized to override the window.___navigate method Gatsby defines and uses to report what path a Link would be taking us to if it wasn't inside a storybook
19 | window.___navigate = (pathname) => {
20 | action('NavigateTo:')(pathname)
21 | }
22 |
23 | addDecorator((Story) => (
24 |
25 |
26 |
27 |
28 |
29 | ))
30 |
31 | addParameters({
32 | docs: {
33 | container: DocsContainer,
34 | page: DocsPage,
35 | },
36 | colorMode: {
37 | modes: {
38 | dark: {
39 | name: 'Dark',
40 | },
41 | },
42 | },
43 | })
44 |
45 | addDecorator(withThemeProvider(theme))
46 |
--------------------------------------------------------------------------------
/src/components/Button/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Link, RichText } from 'prismic-reactjs'
4 | import { Box } from 'theme-ui'
5 |
6 | function Button({ link, label, linkResolver, ...props }) {
7 | const buttonStyles = {
8 | p: '1em 3em',
9 | textAlign: 'center',
10 | width: 'auto',
11 | display: ['block', 'inline-block'],
12 | fontSize: 'tiny',
13 | lineHeight: 'button',
14 | textDecoration: 'none',
15 | cursor: 'pointer',
16 | borderRadius: 'button',
17 | border: 'none',
18 | transition: 'all 0.1s linear',
19 | '&:focus': {
20 | outline: '3px solid currentColor',
21 | outlineOffset: '3px',
22 | },
23 | }
24 |
25 | const elementType = link ? 'a' : 'button'
26 | const linkProps = link ? { href: Link.url(link, linkResolver) } : {}
27 |
28 | return (
29 |
37 | {typeof label === 'string' ? label : }
38 |
39 | )
40 | }
41 |
42 | Button.defaultProps = {
43 | linkResolver: () => {
44 | return '/404'
45 | },
46 | link: null,
47 | label: null,
48 | }
49 |
50 | Button.propTypes = {
51 | link: PropTypes.shape({}),
52 | label: PropTypes.string,
53 | linkResolver: PropTypes.func,
54 | }
55 |
56 | export default Button
57 |
--------------------------------------------------------------------------------
/src/slices/TestimonialsSlider/README.md:
--------------------------------------------------------------------------------
1 | # Testimonials Slider Section
2 |
3 | ### Purpose of the component
4 |
5 | This component allows you to easily create a beautifully styled testominals slider section for your company website.
6 |
7 | ### Variations
8 |
9 | 1. Default
10 | This default version on the component is the only variation.
11 |
12 | ### Properties
13 |
14 | ```
15 | | Property | Type | Repeatable | Description | Required | Default |
16 | | ------------------ | -------------------- | -----------| -------------------------------------- | -------- | ---------------- |
17 | | eyebrow_headline | RichText \|\| String | false | A short headline for the slice | true | -- |
18 | | primary.title | RichText \|\| String | false | A title for the slice | true | -- |
19 | | description | RichText \|\| String | false | A paragraph for the slice | true | -- |
20 | | testimonial | RichText \|\| String | true | The testimonial paragraph for each card| true | -- |
21 | | image | Image \|\| URL | true | A price for the plan | true | -- |
22 | | person | Text \|\| String | true | Name of who gave the testimonial | true | -- |
23 | | title | Text \|\| String | true | A description of the person | true | -- |
24 | ```
25 |
--------------------------------------------------------------------------------
/src/components/Tabs/Tabs.js:
--------------------------------------------------------------------------------
1 | import React, { Children, cloneElement, useState } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 | import { Grid } from '..'
5 |
6 | const Tabs = ({
7 | activeIndex: propsActiveIndex,
8 | children,
9 | variant,
10 | ...props
11 | }) => {
12 | const [activeIndex, setActiveIndex] = useState(propsActiveIndex || 0)
13 | const activateTab = (index) => setActiveIndex(index)
14 |
15 | let activeContent
16 |
17 | const tabs = Children.map(children, (tab, index) => {
18 | if (!tab) return undefined
19 |
20 | const tabProps = tab.props || {}
21 | const isTabActive = index === activeIndex
22 |
23 | if (isTabActive) {
24 | activeContent = tabProps.children
25 | }
26 |
27 | return cloneElement(tab, {
28 | active: isTabActive,
29 | onActivate: () => activateTab(index),
30 | variant,
31 | })
32 | })
33 |
34 | return (
35 |
36 |
37 |
38 | {activeContent}
39 |
40 |
41 | {tabs}
42 |
43 |
44 |
45 | )
46 | }
47 |
48 | Tabs.defaultProps = {
49 | activeIndex: 0,
50 | variant: 'default',
51 | }
52 |
53 | Tabs.propTypes = {
54 | activeIndex: PropTypes.number,
55 | children: PropTypes.node.isRequired,
56 | variant: PropTypes.string,
57 | }
58 |
59 | export default Tabs
60 |
--------------------------------------------------------------------------------
/src/components/GridLayout/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 |
5 | const px = (n) => (typeof n === 'number' ? `${n}px` : n)
6 |
7 | const widthToColumns = (width) =>
8 | Array.isArray(width)
9 | ? width.map(widthToColumns)
10 | : !!width && `repeat(auto-fit, minmax(${px(width)}, 1fr))`
11 |
12 | const countToColumns = (n) =>
13 | Array.isArray(n)
14 | ? n.map(countToColumns)
15 | : !!n && (typeof n === 'number' ? `repeat(${n}, 1fr)` : n)
16 |
17 | const columnOptions = (columns) =>
18 | columns ? countToColumns(columns) : 'repeat(auto-fit, minmax(150px, 1fr))'
19 |
20 | const Grid = React.forwardRef(
21 | ({ width, columns, rowGap, columnGap, ...props }, ref) => {
22 | const gridTemplateColumns = width
23 | ? widthToColumns(width)
24 | : columnOptions(columns)
25 |
26 | return (
27 |
39 | )
40 | }
41 | )
42 |
43 | Grid.defaultProps = {
44 | columns: undefined,
45 | columnGap: undefined,
46 | rowGap: undefined,
47 | width: undefined,
48 | }
49 |
50 | Grid.propTypes = {
51 | columns: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
52 | columnGap: PropTypes.number,
53 | rowGap: PropTypes.number,
54 | width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
55 | }
56 |
57 | export default Grid
58 |
--------------------------------------------------------------------------------
/src/slices/FaqSection/README.md:
--------------------------------------------------------------------------------
1 | # FAQ Section
2 |
3 | ### Purpose of the component
4 |
5 | Component with repeatable dropdown section for creating FAQ sections.
6 | The accordion uses progressive enhancement as an approach to build the interactivity of the component.
7 | This means that the accordion starts out as a non-interactive component, and then interactivity and appropriate styles are added to it when the JavaScript runs.
8 |
9 | ### Variations
10 |
11 | 1. Default
12 | This default version on the component use the simple dropdowns to be used where and as you require.
13 | 2. Faq
14 | This version is a more complete website section, including a title and description to create a a basic FAQ page.
15 | 3. With Image
16 | Much like the full section but using the optional image for more destinct styling.
17 |
18 | ### Properties
19 |
20 | ```
21 | | Property | Type | Repeatable | Description | Required |
22 | | ---------------- | -------------------- | -----------| ------------------------------ | -------- |
23 | | eyebrow_headline | RichText \|\| String | false | A short headline for the slice | true |
24 | | title | RichText \|\| String | false | A title for the slice | true |
25 | | description | RichText \|\| String | false | A paragraph for the slice | true |
26 | | optional_image | Image \|\| URL | false | An Icon image for the slice | false |
27 | | title | RichText \|\| String | true | A title for the dropdowns | true |
28 | | text | RichText \|\| String | true | A paragraph for the dropdowns | true |
29 | ```
30 |
--------------------------------------------------------------------------------
/src/slices/PricingTable/README.md:
--------------------------------------------------------------------------------
1 | # Pricing Table Section
2 |
3 | ### Purpose of the component
4 |
5 | This component allows you to easily create a beautifully styled pricing table section for your company website.
6 |
7 | ### Variations
8 |
9 | 1. Default
10 | This default version on the component is the only variation.
11 |
12 | ### Properties
13 |
14 | ```
15 | | Property | Type | Repeatable | Description | Required | Default |
16 | | ------------------ | -------------------- | -----------| -------------------------------------- | -------- | ---------------- |
17 | | eyebrow_headline | RichText \|\| String | false | A short headline for the slice | true | -- |
18 | | title | RichText \|\| String | false | A title for the slice | true | -- |
19 | | description | RichText \|\| String | false | A paragraph for the slice | true | -- |
20 | | plan_title | RichText \|\| String | true | A title for the plan | true | -- |
21 | | price_option | RichText \|\| String | true | A price for the plan | true | -- |
22 | | features | RichText \|\| String | true | List of features for the plan | true | -- |
23 | | call_to_action_text| Key Text \|\| String | true | A label for the CTA | true | -- |
24 | | call_to_action | String \|\| URL | true | Call to action link | false | https://test.com |
25 | ```
26 |
--------------------------------------------------------------------------------
/src/components/Card/mock.json:
--------------------------------------------------------------------------------
1 | {
2 | "cardsCarousel": {
3 | "image": {
4 | "dimensions": {
5 | "width": 2048,
6 | "height": 1536
7 | },
8 | "alt": "Title 1",
9 | "copyright": null,
10 | "url": "https://images.prismic.io/slicesexamples/b4aeb1e2-e9c1-43ab-82ab-0d0519f9c3cd_float.png?auto=compress,format"
11 | },
12 | "title": [
13 | {
14 | "type": "heading3",
15 | "text": "Title 1",
16 | "spans": []
17 | }
18 | ],
19 | "content": [
20 | {
21 | "type": "paragraph",
22 | "text": "Content hello",
23 | "spans": []
24 | }
25 | ],
26 | "additional_info": [
27 | {
28 | "type": "paragraph",
29 | "text": "",
30 | "spans": []
31 | }
32 | ]
33 | },
34 | "testimonialsSlider": {
35 | "image": {
36 | "dimensions": {
37 | "width": 400,
38 | "height": 400
39 | },
40 | "alt": "Image 1",
41 | "copyright": null,
42 | "url": "https://images.prismic.io/slicesexamples/342cc816-3eeb-403c-a63b-79a678921258_OTg3ODQuanBn.jpg?auto=compress,format&rect=0,0,512,512&w=400&h=400"
43 | },
44 | "testimonial": [
45 | {
46 | "type": "paragraph",
47 | "text": "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words",
48 | "spans": []
49 | }
50 | ],
51 | "person": "John Doe",
52 | "title": "Software eng. at Prismic"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/slices/CustomerLogos/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { RichText } from 'prismic-reactjs'
4 | import { Box } from 'theme-ui'
5 | import uuid from 'react-uuid'
6 | import { Grid, Slice, Wrap } from '../../components'
7 |
8 | const CustomerLogos = ({ slice }) => {
9 | const { items, primary } = slice
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | {items.map((item) => (
19 |
20 |
27 |
28 | ))}
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | )
38 | }
39 |
40 | CustomerLogos.propTypes = {
41 | slice: PropTypes.shape({
42 | primary: PropTypes.shape({
43 | eyebrow_headline: PropTypes.array,
44 | call_to_action: PropTypes.array,
45 | call_to_action_link: PropTypes.shape({
46 | url: PropTypes.string,
47 | }),
48 | }).isRequired,
49 | items: PropTypes.array.isRequired,
50 | }).isRequired,
51 | }
52 |
53 | export default CustomerLogos
54 |
--------------------------------------------------------------------------------
/src/slices/FaqSection/model.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "Slice",
3 | "fieldset": "FAQ",
4 | "description": "List of common questions + answers",
5 | "icon": "question_answer",
6 | "display": "list",
7 | "non-repeat": {
8 | "eyebrow_headline": {
9 | "type": "StructuredText",
10 | "config": {
11 | "single": "paragraph",
12 | "label": "Eyebrow headline",
13 | "placeholder": "Reinforce your copy with a key-worded text, to be displayed before title"
14 | }
15 | },
16 | "title": {
17 | "type": "StructuredText",
18 | "config": {
19 | "single": "heading2",
20 | "label": "title",
21 | "placeholder": "Title"
22 | }
23 | },
24 | "description": {
25 | "type": "StructuredText",
26 | "config": {
27 | "multi": "paragraph, strong, em, hyperlink",
28 | "allowTargetBlank": true,
29 | "label": "Description"
30 | }
31 | },
32 | "optional_image": {
33 | "type": "Image",
34 | "config": {
35 | "constraint": {},
36 | "thumbnails": [],
37 | "label": "Optional image"
38 | }
39 | }
40 | },
41 | "repeat": {
42 | "title": {
43 | "type": "StructuredText",
44 | "config": {
45 | "single": "heading3",
46 | "label": "Title",
47 | "placeholder": "Which browsers do you support?"
48 | }
49 | },
50 | "text": {
51 | "type": "StructuredText",
52 | "config": {
53 | "multi": "paragraph, preformatted, strong, em, hyperlink, list-item, o-list-item",
54 | "allowTargetBlank": true,
55 | "label": "Text",
56 | "placeholder": "The answer to the question"
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/slices/ImagesSlider/model.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "Slice",
3 | "fieldset": "Images Slider",
4 | "description": "A slider of full-width images + description",
5 | "icon": "image",
6 | "display": "list",
7 | "non-repeat": {
8 | "eyebrow_headline": {
9 | "type": "StructuredText",
10 | "config": {
11 | "multi": "paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item",
12 | "allowTargetBlank": true,
13 | "label": "Eyebrow headline"
14 | }
15 | },
16 | "title": {
17 | "type": "StructuredText",
18 | "config": {
19 | "single": "heading1, heading2, heading3, heading4, heading5, heading6",
20 | "label": "Title"
21 | }
22 | },
23 | "description": {
24 | "type": "StructuredText",
25 | "config": {
26 | "multi": "paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item",
27 | "allowTargetBlank": true,
28 | "label": "Description"
29 | }
30 | }
31 | },
32 | "repeat": {
33 | "image": {
34 | "type": "Image",
35 | "config": {
36 | "constraint": {
37 | "width": null,
38 | "height": null
39 | },
40 | "thumbnails": [],
41 | "label": "Full Width Image"
42 | }
43 | },
44 | "description": {
45 | "type": "StructuredText",
46 | "config": {
47 | "multi": "paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item",
48 | "allowTargetBlank": true,
49 | "label": "Description"
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/slices/CallToAction/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { array, func, shape } from 'prop-types'
3 | import { RichText } from 'prismic-reactjs'
4 | import { Box } from 'theme-ui'
5 |
6 | import { Button, Wrap, Desc, Slice } from '../../components'
7 |
8 | const CallToAction = ({
9 | slice: { primary },
10 | linkResolver, // linkResolver is pased by SliceZone
11 | }) => {
12 | const styles = {
13 | header: {
14 | mb: ['hPaddingHalf', null, 'hPadding'],
15 | },
16 | icon: {
17 | mb: 'cMargin',
18 | mx: 'auto',
19 | img: {
20 | width: '120px',
21 | },
22 | },
23 | }
24 |
25 | return (
26 |
27 |
28 |
29 | {primary.icon_image && (
30 |
31 |
36 |
37 | )}
38 |
39 |
40 |
41 | {primary.paragraph && (
42 |
43 |
44 |
45 | )}
46 |
47 |
53 |
54 |
55 | )
56 | }
57 |
58 | CallToAction.propTypes = {
59 | slice: shape({
60 | primary: shape({
61 | title: array.isRequired,
62 | }).isRequired,
63 | }).isRequired,
64 | linkResolver: func,
65 | }
66 |
67 | CallToAction.defaultProps = {
68 | linkResolver: () => {
69 | return '/404'
70 | },
71 | }
72 |
73 | export default CallToAction
74 |
--------------------------------------------------------------------------------
/src/slices/ImagesSlider/mock.json:
--------------------------------------------------------------------------------
1 | {
2 | "slice_type": "images_slider",
3 | "slice_label": null,
4 | "items": [
5 | {
6 | "image": {
7 | "dimensions": {
8 | "width": 5184,
9 | "height": 3456
10 | },
11 | "alt": "Alt text",
12 | "copyright": null,
13 | "url": "https://images.prismic.io/slicesexamples%2F995a4f19-2830-43ee-85cf-89f3c8dd1f1b_aj-robbie-t5v1rup9dcy-unsplash.jpg?auto=compress,format"
14 | },
15 | "description": [
16 | {
17 | "type": "paragraph",
18 | "text": "This one is an elephant. Simply dummy text of the printing and typesetting industry.",
19 | "spans": []
20 | }
21 | ]
22 | },
23 | {
24 | "image": {
25 | "dimensions": {
26 | "width": 720,
27 | "height": 432
28 | },
29 | "alt": "Alt text",
30 | "copyright": null,
31 | "url": "https://images.prismic.io/slicesexamples%2F0ee40f69-8329-4c8f-83d0-f9db9391f9d8_caramel_slices.jpg?auto=compress,format"
32 | },
33 | "description": [
34 | {
35 | "type": "paragraph",
36 | "text": "This one is a cake I guess? Simply dummy text of the printing and typesetting industry.",
37 | "spans": []
38 | }
39 | ]
40 | }
41 | ],
42 | "primary": {
43 | "eyebrow_headline": [
44 | {
45 | "type": "paragraph",
46 | "text": "Eyebrow",
47 | "spans": []
48 | }
49 | ],
50 | "title": [
51 | {
52 | "type": "heading1",
53 | "text": "This is it. The slider.",
54 | "spans": []
55 | }
56 | ],
57 | "description": [
58 | {
59 | "type": "paragraph",
60 | "text": "Simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s.",
61 | "spans": []
62 | }
63 | ]
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/components/Icon/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 | import Icon from '.'
5 |
6 | export default {
7 | title: 'Components/Icon',
8 | component: Icon,
9 | }
10 | const styles = {
11 | span: {
12 | width: '1em',
13 | height: '1em',
14 | },
15 | '.feature:before': {
16 | content: `url('${Icon.encode('feature')}')`,
17 | },
18 | '.not-included:before': {
19 | content: `url('${Icon.encode('notIncluded')}')`,
20 | },
21 | '.play:before': {
22 | content: `url('${Icon.encode('play')}')`,
23 | },
24 | '.playBlack:before': {
25 | content: `url('${Icon.encode('playBlack')}')`,
26 | },
27 | }
28 |
29 | const CreateBox = ({ name, children }) => (
30 |
31 | {children}
32 |
33 | {name}
34 |
35 | )
36 |
37 | CreateBox.propTypes = {
38 | name: PropTypes.string.isRequired,
39 | children: PropTypes.node.isRequired,
40 | }
41 |
42 | export const Default = () => (
43 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | )
75 |
--------------------------------------------------------------------------------
/src/slices/VideoHighlights/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { RichText } from 'prismic-reactjs'
4 | import { Box } from 'theme-ui'
5 | import uuid from 'react-uuid'
6 | import { Desc, Title, Slice, Tabs, Video, Wrap } from '../../components'
7 | import { structuredTextPropTypes } from '../../utils/prop-types'
8 | import tabItem from './styles'
9 |
10 | const VideoHighlights = ({ slice }) => {
11 | const { items, primary } = slice
12 | return (
13 |
14 |
15 |
16 |
17 |
18 |
19 | {primary.description && (
20 |
21 |
22 |
23 | )}
24 |
25 |
26 |
27 | {items.map((item) => {
28 | const tabLabel = (
29 |
30 | {RichText.asText(item.video_title)}
31 |
32 | )
33 |
34 | return (
35 |
36 |
37 |
38 | )
39 | })}
40 |
41 |
42 |
43 |
44 |
45 | )
46 | }
47 |
48 | VideoHighlights.propTypes = {
49 | slice: PropTypes.shape({
50 | primary: PropTypes.shape({
51 | title: structuredTextPropTypes,
52 | description: structuredTextPropTypes,
53 | eyebrow_headline: PropTypes.array,
54 | call_to_action: PropTypes.array,
55 | call_to_action_link: PropTypes.shape({
56 | url: PropTypes.string,
57 | }),
58 | }).isRequired,
59 | items: PropTypes.array.isRequired,
60 | }).isRequired,
61 | }
62 |
63 | export default VideoHighlights
64 |
--------------------------------------------------------------------------------
/src/components/Card/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Box } from 'theme-ui'
3 | import { RichText } from 'prismic-reactjs'
4 |
5 | import Card from '.'
6 | import { cardsCarousel, testimonialsSlider } from './mock.json'
7 |
8 | export default {
9 | title: 'Components/Card',
10 | component: Card,
11 | }
12 |
13 | export const CardsCarousel = () => (
14 |
15 |
16 |
17 |
18 |
19 | {RichText.asText(cardsCarousel.title)}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | )
28 |
29 | export const TestimonialsSlider = () => (
30 |
31 |
35 |
36 |
37 |
46 |
47 |
48 |
49 |
50 |
59 | {testimonialsSlider.person}
60 |
61 |
62 | {testimonialsSlider.title}
63 |
64 |
65 |
66 |
67 | )
68 |
--------------------------------------------------------------------------------
/src/slices/AlternateGrid/model.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "Slice",
3 | "fieldset": "AlternateGrid",
4 | "description": "A predesigned Text Grid with Image left or right",
5 | "icon": "wrap_text",
6 | "display": "list",
7 | "non-repeat": {
8 | "eyebrow_headline": {
9 | "type": "StructuredText",
10 | "config": {
11 | "multi": "paragraph, preformatted, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item",
12 | "allowTargetBlank": true,
13 | "label": "Eyebrow headline"
14 | }
15 | },
16 | "title": {
17 | "type": "StructuredText",
18 | "config": {
19 | "single": "heading1, heading2, heading3, heading4, heading5, heading6",
20 | "label": "title"
21 | }
22 | },
23 | "description": {
24 | "type": "StructuredText",
25 | "config": {
26 | "multi": "paragraph, preformatted, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item",
27 | "allowTargetBlank": true,
28 | "label": "description"
29 | }
30 | },
31 | "optional_image": {
32 | "type": "Image",
33 | "config": {
34 | "constraint": {},
35 | "thumbnails": [],
36 | "label": "Optional image"
37 | }
38 | },
39 | "image_side": {
40 | "type": "Select",
41 | "config": {
42 | "options": ["left", "right"],
43 | "default_value": "left",
44 | "label": "Image side"
45 | }
46 | }
47 | },
48 | "repeat": {
49 | "optional_icon": {
50 | "type": "Image",
51 | "config": {
52 | "constraint": {},
53 | "thumbnails": [],
54 | "label": "Optional Icon"
55 | }
56 | },
57 | "title": {
58 | "type": "StructuredText",
59 | "config": {
60 | "single": "heading3",
61 | "label": "title"
62 | }
63 | },
64 | "description": {
65 | "type": "StructuredText",
66 | "config": {
67 | "multi": "paragraph, preformatted, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item",
68 | "allowTargetBlank": true,
69 | "label": "description"
70 | }
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/slices/PricingTable/model.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "Slice",
3 | "fieldset": "Pricing Table",
4 | "description": "Display a list of pricing plans",
5 | "icon": "attach_money",
6 | "display": "grid",
7 | "non-repeat": {
8 | "eyebrow_headline": {
9 | "type": "StructuredText",
10 | "config": {
11 | "single": "paragraph",
12 | "label": "Eyebrow Headline",
13 | "placeholder": "Pricing plans"
14 | }
15 | },
16 | "title": {
17 | "type": "StructuredText",
18 | "config": {
19 | "single": "heading2",
20 | "label": "Title",
21 | "placeholder": "Choose the plan"
22 | }
23 | },
24 | "description": {
25 | "type": "StructuredText",
26 | "config": {
27 | "multi": "paragraph, strong, em, hyperlink",
28 | "allowTargetBlank": true,
29 | "label": "Description",
30 | "placeholder": "Choose the version that works for you ..."
31 | }
32 | }
33 | },
34 | "repeat": {
35 | "plan_title": {
36 | "type": "StructuredText",
37 | "config": {
38 | "single": "heading3",
39 | "label": "Plan title",
40 | "placeholder": "Simple, Gold, Premium..."
41 | }
42 | },
43 | "price_option": {
44 | "type": "StructuredText",
45 | "config": {
46 | "multi": "heading4",
47 | "label": "Price Option",
48 | "placeholder": "Free, $19, Contact us..."
49 | }
50 | },
51 | "features": {
52 | "type": "StructuredText",
53 | "config": {
54 | "multi": "list-item",
55 | "label": "Features",
56 | "placeholder": "A list of features using bullet list"
57 | }
58 | },
59 | "call_to_action": {
60 | "type": "Link",
61 | "config": {
62 | "allowTargetBlank": true,
63 | "label": "Call To Action",
64 | "placeholder": "Link to Signup / More info..."
65 | }
66 | },
67 | "call_to_action_text": {
68 | "type": "StructuredText",
69 | "config": {
70 | "multi": "paragraph",
71 | "label": "Call To Action Text",
72 | "placeholder": "Start your free trial"
73 | }
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/slices/CustomerLogos/mock.json:
--------------------------------------------------------------------------------
1 | {
2 | "slice_type": "customer_logos",
3 | "slice_label": null,
4 | "items": [
5 | {
6 | "logo": {
7 | "dimensions": { "width": 144, "height": 38 },
8 | "alt": null,
9 | "copyright": null,
10 | "url": "https://images.prismic.io/slicesexamples/95a608cd-8d3c-40c1-a554-0bec1b89eda5_logo-3.svg?auto=compress,format"
11 | },
12 | "link": { "link_type": "Any" }
13 | },
14 | {
15 | "logo": {
16 | "dimensions": { "width": 116, "height": 19 },
17 | "alt": null,
18 | "copyright": null,
19 | "url": "https://images.prismic.io/slicesexamples/b62a8629-f7f1-4d2e-885a-bd7c6bcff201_logo-2.svg?auto=compress,format"
20 | },
21 | "link": { "link_type": "Any" }
22 | },
23 | {
24 | "logo": {
25 | "dimensions": { "width": 137, "height": 23 },
26 | "alt": null,
27 | "copyright": null,
28 | "url": "https://images.prismic.io/slicesexamples/23461395-458d-41f5-be25-bac37a4ff53e_logo-6.svg?auto=compress,format"
29 | },
30 | "link": { "link_type": "Any" }
31 | },
32 | {
33 | "logo": {
34 | "dimensions": { "width": 92, "height": 30 },
35 | "alt": null,
36 | "copyright": null,
37 | "url": "https://images.prismic.io/slicesexamples/4360a72b-bfe4-4144-911c-597146f72c00_logo-5.svg?auto=compress,format"
38 | },
39 | "link": { "link_type": "Any" }
40 | },
41 | {
42 | "logo": {
43 | "dimensions": { "width": 92, "height": 25 },
44 | "alt": null,
45 | "copyright": null,
46 | "url": "https://images.prismic.io/slicesexamples/846576e2-c93c-40dd-8aaa-b97f4e99e7aa_logo-1.svg?auto=compress,format"
47 | },
48 | "link": { "link_type": "Any" }
49 | }
50 | ],
51 | "primary": {
52 | "eyebrow_headline": [
53 | { "type": "heading2", "text": "Trusted by", "spans": [] }
54 | ],
55 | "call_to_action": [
56 | { "type": "paragraph", "text": "View customer stories", "spans": [] }
57 | ],
58 | "call_to_action_link": {
59 | "link_type": "Web",
60 | "url": "https://prismic.io",
61 | "target": "_blank"
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/slices/TestimonialsSlider/model.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "Slice",
3 | "fieldset": "Testimonials Slider",
4 | "description": "A predesigned Slider with rich testimonial",
5 | "icon": "notifications_active",
6 | "display": "list",
7 | "non-repeat": {
8 | "eyebrow_headline": {
9 | "type": "StructuredText",
10 | "config": {
11 | "multi": "paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item",
12 | "allowTargetBlank": true,
13 | "label": "Eyebrow headline"
14 | }
15 | },
16 | "title": {
17 | "type": "StructuredText",
18 | "config": {
19 | "single": "heading1, heading2, heading3, heading4, heading5, heading6",
20 | "label": "Title"
21 | }
22 | },
23 | "paragraph": {
24 | "type": "StructuredText",
25 | "config": {
26 | "multi": "paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item",
27 | "label": "Paragraph"
28 | }
29 | },
30 | "button_link": {
31 | "type": "Link",
32 | "config": {
33 | "label": "Button Link"
34 | }
35 | },
36 | "button_label": {
37 | "type": "Text",
38 | "config": {
39 | "label": "Button Label",
40 | "placeholder": "Text for button"
41 | }
42 | }
43 | },
44 | "repeat": {
45 | "image": {
46 | "type": "Image",
47 | "config": {
48 | "constraint": {},
49 | "thumbnails": [],
50 | "label": "image"
51 | }
52 | },
53 | "testimonial": {
54 | "type": "StructuredText",
55 | "config": {
56 | "multi": "paragraph, preformatted, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item",
57 | "label": "testimonial"
58 | }
59 | },
60 | "person": {
61 | "type": "Text",
62 | "config": {
63 | "label": "person",
64 | "placeholder": "Their full name"
65 | }
66 | },
67 | "title": {
68 | "type": "Text",
69 | "config": {
70 | "label": "title",
71 | "placeholder": "Their title"
72 | }
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/slices/FaqSection/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { RichText } from 'prismic-reactjs'
4 | import { Box } from 'theme-ui'
5 | import {
6 | Accordion,
7 | Desc,
8 | Eyebrow,
9 | Grid,
10 | Head,
11 | Title,
12 | Slice,
13 | Wrap,
14 | } from '../../components'
15 | import { structuredTextPropTypes } from '../../utils/prop-types'
16 |
17 | const FaqSection = ({ slice }) => {
18 | const { items, primary } = slice
19 |
20 | return (
21 |
22 |
23 |
24 |
25 | {primary.eyebrow_headline && (
26 |
27 |
28 |
29 | )}
30 | {primary.title && (
31 |
32 |
33 |
34 | )}
35 | {primary.description && (
36 |
37 |
38 |
39 | )}
40 |
41 |
42 | {primary.optional_image && (
43 |
44 | )}
45 |
46 | {items.map(({ text, title }) => {
47 | return (
48 |
55 | )
56 | })}
57 |
58 |
59 |
60 |
61 |
62 | )
63 | }
64 |
65 | FaqSection.propTypes = {
66 | slice: PropTypes.shape({
67 | primary: PropTypes.shape({
68 | title: structuredTextPropTypes,
69 | description: structuredTextPropTypes,
70 | eyebrow_headline: structuredTextPropTypes,
71 | optional_image: PropTypes.shape({
72 | url: PropTypes.string,
73 | }),
74 | }).isRequired,
75 | items: PropTypes.array.isRequired,
76 | }).isRequired,
77 | }
78 |
79 | export default FaqSection
80 |
--------------------------------------------------------------------------------
/src/slices/CardsCarousel/model.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "Slice",
3 | "fieldset": "Cards Carousel",
4 | "description": "A carousel with text + image cards",
5 | "icon": "image",
6 | "display": "list",
7 | "non-repeat": {
8 | "eyebrow_headline": {
9 | "type": "StructuredText",
10 | "config": {
11 | "multi": "paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item",
12 | "allowTargetBlank": true,
13 | "label": "Eyebrow headline"
14 | }
15 | },
16 | "title": {
17 | "type": "StructuredText",
18 | "config": {
19 | "single": "heading1, heading2, heading3, heading4, heading5, heading6",
20 | "label": "Title"
21 | }
22 | },
23 | "description": {
24 | "type": "StructuredText",
25 | "config": {
26 | "multi": "paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item",
27 | "allowTargetBlank": true,
28 | "label": "Description"
29 | }
30 | }
31 | },
32 | "repeat": {
33 | "image": {
34 | "type": "Image",
35 | "config": {
36 | "constraint": {
37 | "width": null,
38 | "height": null
39 | },
40 | "thumbnails": [],
41 | "label": "Image"
42 | }
43 | },
44 | "title": {
45 | "type": "StructuredText",
46 | "config": {
47 | "single": "heading3",
48 | "label": "Title"
49 | }
50 | },
51 | "content": {
52 | "type": "StructuredText",
53 | "config": {
54 | "multi": "paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item",
55 | "allowTargetBlank": true,
56 | "label": "Content"
57 | }
58 | },
59 | "additional_info": {
60 | "type": "StructuredText",
61 | "config": {
62 | "multi": "paragraph, preformatted, heading3, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item",
63 | "allowTargetBlank": true,
64 | "label": "Additional Info",
65 | "placeholder": "eg. name of person in testimonial"
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # Snowpack dependency directory (https://snowpack.dev/)
45 | web_modules/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 | .parcel-cache
78 |
79 | # Next.js build output
80 | .next
81 | out
82 |
83 | # Nuxt.js build / generate output
84 | .nuxt
85 | dist
86 |
87 | # Gatsby files
88 | .cache/
89 | # Comment in the public line in if your project uses Gatsby and not Next.js
90 | # https://nextjs.org/blog/next-9-1#public-directory-support
91 | # public
92 |
93 | # vuepress build output
94 | .vuepress/dist
95 |
96 | # Serverless directories
97 | .serverless/
98 |
99 | # FuseBox cache
100 | .fusebox/
101 |
102 | # DynamoDB Local files
103 | .dynamodb/
104 |
105 | # TernJS port file
106 | .tern-port
107 |
108 | # Stores VSCode versions used for testing VSCode extensions
109 | .vscode-test
110 |
111 | # yarn v2
112 |
113 | .yarn/cache
114 | .yarn/unplugged
115 | .yarn/build-state.yml
116 | .pnp.*
117 | Terms
118 |
119 | .vercel
120 | .netlify
121 | storybook-static
122 |
123 | # Misc
124 | .DS_Store
125 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "essential-slices",
3 | "version": "1.0.4",
4 | "description": "",
5 | "source": "src/index.js",
6 | "main": "dist/index.js",
7 | "module": "dist/index.module.js",
8 | "unpkg": "dist/index.umd.js",
9 | "scripts": {
10 | "test": "echo \"Error: no test specified\" && exit 1",
11 | "storybook": "start-storybook -p 6006",
12 | "build-storybook": "build-storybook",
13 | "build": "microbundle --jsx React.createElement && yarn run bundle",
14 | "bundle": "node ./node_modules/sm-commons/scripts/bundle",
15 | "lint": "eslint .",
16 | "lint:fix": "eslint . --fix",
17 | "format": "prettier --write \"**/*.{js,jsx,json,md}\"",
18 | "prepare": "husky install"
19 | },
20 | "eslintConfig": {
21 | "extends": "react-app"
22 | },
23 | "repository": {
24 | "type": "git",
25 | "url": "git+https://github.com/prismicio/essential-slices.git"
26 | },
27 | "keywords": [],
28 | "author": "",
29 | "license": "ISC",
30 | "bugs": {
31 | "url": "https://github.com/prismicio/essential-slices/issues"
32 | },
33 | "homepage": "https://github.com/prismicio/essential-slices#readme",
34 | "dependencies": {
35 | "react-slick": "^0.28.1",
36 | "react-uuid": "^1.0.2"
37 | },
38 | "devDependencies": {
39 | "@babel/core": "^7.10.2",
40 | "@storybook/addon-a11y": "^5.3.19",
41 | "@storybook/addon-actions": "^5.3.19",
42 | "@storybook/addon-docs": "^5.3.19",
43 | "@storybook/addon-links": "^5.3.19",
44 | "@storybook/addon-viewport": "^5.3.19",
45 | "@storybook/addons": "^5.3.19",
46 | "@storybook/react": "^5.3.19",
47 | "babel-loader": "^8.1.0",
48 | "eslint": "^7.1.0",
49 | "eslint-config-airbnb": "^18.1.0",
50 | "eslint-config-prettier": "^8.1.0",
51 | "eslint-plugin-import": "^2.20.2",
52 | "eslint-plugin-jsx-a11y": "^6.2.3",
53 | "eslint-plugin-prettier": "^3.1.3",
54 | "eslint-plugin-react": "^7.20.0",
55 | "husky": "^6.0.0",
56 | "lint-staged": "^10.2.8",
57 | "microbundle": "^0.13.0",
58 | "prettier": "^2.0.5",
59 | "prismic-reactjs": "^1.3.1",
60 | "prop-types": "^15.7.2",
61 | "react": "^17.0.2",
62 | "react-dom": "^17.0.2",
63 | "sm-commons": "^0.0.23",
64 | "storybook-addon-color-mode": "^1.2.5",
65 | "theme-ui": "^0.6.2"
66 | },
67 | "peerDependencies": {
68 | "prismic-reactjs": "^1.3.1",
69 | "prop-types": "^15.7.2",
70 | "react": "^16.13.1 || 17",
71 | "react-dom": "^16.13.1 || 17",
72 | "theme-ui": "^0.6.2"
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/components/Slider/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Box } from 'theme-ui'
3 | import Slider from '.'
4 |
5 | export default {
6 | title: 'Components/Slider',
7 | component: Slider,
8 | }
9 |
10 | const CreateBoxes = (n) => {
11 | const items = []
12 |
13 | for (let i = 0; i < n; i += 1) {
14 | items.push(
15 |
16 |
24 | {`Content #${i + 1}`}
25 |
26 |
27 | )
28 | }
29 |
30 | return items
31 | }
32 |
33 | export const Default = () => (
34 |
35 |
36 | {CreateBoxes(5)}
37 |
38 |
39 | )
40 |
41 | export const WithDots = () => (
42 |
43 |
52 | {CreateBoxes(5)}
53 |
54 |
55 | )
56 |
57 | export const Responsive = () => (
58 |
59 |
99 | {CreateBoxes(10)}
100 |
101 |
102 | )
103 |
--------------------------------------------------------------------------------
/src/components/Slider/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 | import SlickSlider from 'react-slick'
5 |
6 | import { arrowStyles, dotContainerStyles, slickStyles } from './styles'
7 | import DotsWithLabel from './DotsWithLabel'
8 | import Icon from '../Icon'
9 |
10 | const NextArrow = (props) => {
11 | const { onClick } = props
12 |
13 | return (
14 |
26 |
27 |
28 | )
29 | }
30 |
31 | NextArrow.propTypes = {
32 | onClick: PropTypes.func.isRequired,
33 | }
34 |
35 | const PrevArrow = (props) => {
36 | const { onClick } = props
37 |
38 | return (
39 |
51 |
52 |
53 | )
54 | }
55 |
56 | PrevArrow.propTypes = {
57 | onClick: PropTypes.func.isRequired,
58 | }
59 |
60 | const AppendDots = (dots) => {
61 | return (
62 |
63 |
64 |
65 | )
66 | }
67 |
68 | const Slider = ({
69 | children,
70 | speed,
71 | draggable,
72 | dotsWithLabel,
73 | dotsLabel,
74 | ...props
75 | }) => {
76 | const customPagingProp = dotsWithLabel
77 | ? { customPaging: (i) => DotsWithLabel(i, dotsLabel) }
78 | : {}
79 |
80 | return (
81 |
82 | }
86 | prevArrow={}
87 | speed={speed}
88 | {...customPagingProp}
89 | {...props}
90 | >
91 | {children}
92 |
93 |
94 | )
95 | }
96 |
97 | Slider.defaultProps = {
98 | speed: 500,
99 | dotsWithLabel: false,
100 | draggable: false,
101 | dotsLabel: 'Slide',
102 | }
103 |
104 | Slider.propTypes = {
105 | children: PropTypes.node.isRequired,
106 | dotsWithLabel: PropTypes.bool,
107 | draggable: PropTypes.bool,
108 | speed: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
109 | dotsLabel: PropTypes.string,
110 | }
111 |
112 | export default Slider
113 |
--------------------------------------------------------------------------------
/src/slices/ImagesSlider/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { RichText } from 'prismic-reactjs'
4 | import uuid from 'react-uuid'
5 | import { Box } from 'theme-ui'
6 |
7 | import { Desc, Eyebrow, Slice, Slider, Wrap } from '../../components'
8 | import { structuredTextPropTypes } from '../../utils/prop-types'
9 |
10 | const ImagesSlider = ({ slice }) => {
11 | const { items, primary } = slice
12 |
13 | const settings = {
14 | dots: true,
15 | dotsWithLabel: true,
16 | arrows: false,
17 | draggable: false,
18 |
19 | slidesToShow: 1,
20 | slidesToScroll: 1,
21 | }
22 |
23 | return (
24 |
25 |
26 |
27 |
28 | {primary.eyebrow_headline && (
29 |
30 |
31 |
32 | )}
33 |
34 |
35 |
36 | {primary.description && (
37 |
38 |
39 |
40 | )}
41 |
42 |
43 |
49 |
50 | {items.map((item) => (
51 |
52 |
61 |
68 | {item.description && (
69 |
70 |
71 |
72 |
73 |
74 | )}
75 |
76 |
77 | ))}
78 |
79 |
80 |
81 |
82 | )
83 | }
84 |
85 | ImagesSlider.propTypes = {
86 | slice: PropTypes.shape({
87 | primary: PropTypes.shape({
88 | eyebrow_headline: structuredTextPropTypes,
89 | title: structuredTextPropTypes,
90 | description: structuredTextPropTypes,
91 | }).isRequired,
92 | items: PropTypes.array.isRequired,
93 | }).isRequired,
94 | }
95 |
96 | export default ImagesSlider
97 |
--------------------------------------------------------------------------------
/src/slices/AlternateGrid/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ThemeProvider } from 'theme-ui'
3 | import theme from '../../theme'
4 | import AlternateGrid from '.'
5 | import mock from './mock.json'
6 |
7 | export default {
8 | title: 'Slices/AlternateGrid',
9 | component: AlternateGrid,
10 | }
11 |
12 | function linkResolver(doc) {
13 | return `/link/to/${doc.uid}`
14 | }
15 |
16 | export const Default = () => (
17 | ({ ...e, optional_icon: null })),
21 | primary: {
22 | ...mock.primary,
23 | call_to_action: null,
24 | },
25 | }}
26 | linkResolver={linkResolver}
27 | />
28 | )
29 |
30 | export const WithIcons = () => (
31 |
41 | )
42 |
43 | export const NoImage = () => (
44 |
55 | )
56 |
57 | export const WithCustomTheme = () => {
58 | const customTheme = {
59 | ...theme,
60 | container: {
61 | ...theme.container,
62 | slice: {
63 | background: '#F7F7F7',
64 | },
65 | eyebrow: {
66 | color: 'tomato',
67 | },
68 | item: {
69 | background: '#FFF',
70 | border: '1px solid #111',
71 | padding: '12px',
72 | },
73 | },
74 | }
75 | return (
76 |
77 |
88 |
89 | )
90 | }
91 |
92 | export const WithCustomSpans = () => {
93 | const customTheme = {
94 | ...theme,
95 | container: {
96 | ...theme.container,
97 | grid: {
98 | spans: '15% 85%',
99 | },
100 | },
101 | }
102 | return (
103 |
104 |
114 |
115 | )
116 | }
117 |
118 | export const WithButtons = () => (
119 | ({ ...e, optional_icon: null })),
123 | primary: {
124 | ...mock.primary,
125 | },
126 | }}
127 | linkResolver={linkResolver}
128 | />
129 | )
130 |
131 | export const WithIconsAndButtons = () => (
132 |
133 | )
134 |
--------------------------------------------------------------------------------
/src/slices/CardsCarousel/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 | import { RichText } from 'prismic-reactjs'
5 | import uuid from 'react-uuid'
6 |
7 | import { Card, Desc, Eyebrow, Slider, Slice, Wrap } from '../../components'
8 |
9 | const CardsCarousel = ({ slice }) => {
10 | const { primary, items } = slice
11 | return (
12 |
13 |
14 |
15 | {primary.eyebrow_headline && (
16 |
17 |
18 |
19 | )}
20 |
21 |
22 | {primary.description && (
23 |
24 |
25 |
26 | )}
27 |
28 |
68 | {items.map((item) => (
69 |
70 |
71 |
72 |
73 |
74 | {RichText.asText(item.title)}
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | ))}
83 |
84 |
85 |
86 |
87 | )
88 | }
89 |
90 | CardsCarousel.propTypes = {
91 | slice: PropTypes.shape({
92 | primary: PropTypes.shape({
93 | eyebrow_headline: PropTypes.array,
94 | title: PropTypes.array,
95 | description: PropTypes.array,
96 | }).isRequired,
97 | items: PropTypes.array.isRequired,
98 | }).isRequired,
99 | }
100 |
101 | export default CardsCarousel
102 |
--------------------------------------------------------------------------------
/src/slices/TestimonialsSlider/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 | import { RichText } from 'prismic-reactjs'
5 | import uuid from 'react-uuid'
6 |
7 | import { structuredTextPropTypes } from '../../utils/prop-types'
8 | import { Card, Desc, Eyebrow, Slider, Slice, Wrap } from '../../components'
9 |
10 | const TestimonialsSlider = ({ slice }) => {
11 | const { primary, items } = slice
12 |
13 | const settings = {
14 | dots: true,
15 | dotsWithLabel: true,
16 | dotsLabel: 'Testimonial',
17 | arrows: true,
18 | draggable: false,
19 | infinite: false,
20 | slidesToShow: 1,
21 | slidesToScroll: 1,
22 | }
23 |
24 | return (
25 |
26 |
27 |
28 | {primary.eyebrow_headline && (
29 |
30 |
31 |
32 | )}
33 |
34 |
35 | {primary.description && (
36 |
37 |
38 |
39 | )}
40 |
41 |
42 | {items.map((item) => (
43 |
44 |
49 |
50 |
51 |
60 |
61 |
62 |
63 |
64 |
73 | {item.person}
74 |
75 |
76 | {item.title}
77 |
78 |
79 |
80 |
81 | ))}
82 |
83 |
84 |
85 |
86 | )
87 | }
88 |
89 | TestimonialsSlider.propTypes = {
90 | slice: PropTypes.shape({
91 | primary: PropTypes.shape({
92 | eyebrow_headline: structuredTextPropTypes,
93 | title: structuredTextPropTypes,
94 | description: structuredTextPropTypes,
95 | }).isRequired,
96 | items: PropTypes.array.isRequired,
97 | }).isRequired,
98 | }
99 |
100 | export default TestimonialsSlider
101 |
--------------------------------------------------------------------------------
/src/components/Accordion/AccordionItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { RichText } from 'prismic-reactjs'
4 | import { Box } from 'theme-ui'
5 | import { Icon } from '..'
6 |
7 | const AccordionItem = ({ active, title, text, onClick, ...props }) => {
8 | return (
9 |
10 |
11 |
12 |
50 |
51 |
65 |
66 |
67 |
68 |
69 | {active && (
70 |
80 |
81 |
82 | )}
83 |
84 |
85 | )
86 | }
87 |
88 | AccordionItem.propTypes = {
89 | active: PropTypes.bool,
90 | title: PropTypes.arrayOf(
91 | PropTypes.shape({
92 | type: PropTypes.string,
93 | text: PropTypes.string,
94 | spans: PropTypes.arrayOf(PropTypes.shape({})),
95 | })
96 | ),
97 | text: PropTypes.arrayOf(
98 | PropTypes.shape({
99 | type: PropTypes.string,
100 | text: PropTypes.string,
101 | spans: PropTypes.arrayOf(PropTypes.shape({})),
102 | })
103 | ),
104 | onClick: PropTypes.func,
105 | }
106 |
107 | AccordionItem.defaultProps = {
108 | active: false,
109 | title: null,
110 | text: null,
111 | onClick: null,
112 | }
113 |
114 | export default AccordionItem
115 |
--------------------------------------------------------------------------------
/src/slices/VideoHighlights/mock.json:
--------------------------------------------------------------------------------
1 | {
2 | "slice_type": "video_highlights",
3 | "slice_label": null,
4 | "items": [
5 | {
6 | "video_title": [
7 | {
8 | "type": "paragraph",
9 | "text": "Single Page Apps - Fat frontend or fat backend, not both",
10 | "spans": []
11 | }
12 | ],
13 | "video_src": {
14 | "type": "video",
15 | "thumbnail_height": 360,
16 | "provider_url": "https://www.youtube.com/",
17 | "thumbnail_url": "https://i.ytimg.com/vi/gYm9Q6J5gp8/hqdefault.jpg",
18 | "html": "",
19 | "provider_name": "YouTube",
20 | "version": "1.0",
21 | "title": "Single Page Apps - Fat frontend or fat backend, not both",
22 | "author_name": "Prismic",
23 | "height": 270,
24 | "author_url": "https://www.youtube.com/channel/UCJq6AEgtWeZt7ziQ-fLKOeA",
25 | "thumbnail_width": 480,
26 | "width": 480,
27 | "embed_url": "https://www.youtube.com/watch?v=gYm9Q6J5gp8"
28 | }
29 | },
30 | {
31 | "video_title": [
32 | {
33 | "type": "paragraph",
34 | "text": "Journey to JavaScript development",
35 | "spans": []
36 | }
37 | ],
38 | "video_src": {
39 | "thumbnail_height": 360,
40 | "thumbnail_url": "https://i.ytimg.com/vi/62C-E6WL0_A/hqdefault.jpg",
41 | "width": 480,
42 | "provider_url": "https://www.youtube.com/",
43 | "type": "video",
44 | "author_name": "Prismic",
45 | "height": 270,
46 | "version": "1.0",
47 | "thumbnail_width": 480,
48 | "title": "Journey to JavaScript development",
49 | "author_url": "https://www.youtube.com/channel/UCJq6AEgtWeZt7ziQ-fLKOeA",
50 | "provider_name": "YouTube",
51 | "html": "",
52 | "embed_url": "https://www.youtube.com/watch?v=62C-E6WL0_A"
53 | }
54 | },
55 | {
56 | "video_title": [
57 | {
58 | "type": "paragraph",
59 | "text": "So many frameworks out there?",
60 | "spans": []
61 | }
62 | ],
63 | "video_src": {
64 | "version": "1.0",
65 | "author_name": "Prismic",
66 | "html": "",
67 | "title": "So many frameworks out there! What should you do?",
68 | "author_url": "https://www.youtube.com/channel/UCJq6AEgtWeZt7ziQ-fLKOeA",
69 | "thumbnail_width": 480,
70 | "thumbnail_height": 360,
71 | "provider_url": "https://www.youtube.com/",
72 | "type": "video",
73 | "provider_name": "YouTube",
74 | "width": 480,
75 | "thumbnail_url": "https://i.ytimg.com/vi/ZAz5_lp_3SI/hqdefault.jpg",
76 | "height": 270,
77 | "embed_url": "https://www.youtube.com/watch?v=ZAz5_lp_3SI"
78 | }
79 | }
80 | ],
81 | "primary": {
82 | "eyebrow_headline": "Feature",
83 | "title": [{ "type": "heading1", "text": "Video Highlights", "spans": [] }],
84 | "description": [
85 | {
86 | "type": "paragraph",
87 | "text": "These are some awesome videos that teach you how to use Prismic Slices to quickly create a great Web site.",
88 | "spans": []
89 | }
90 | ]
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/components/Accordion/mock.json:
--------------------------------------------------------------------------------
1 | {
2 | "slice_type": "faq_section",
3 | "slice_label": null,
4 | "items": [
5 | {
6 | "title": [{ "type": "heading3", "text": "Test tracking", "spans": [] }],
7 | "text": [
8 | {
9 | "type": "paragraph",
10 | "text": "Only use them when the content is appropriate. If the panels are too long, the user will end up scrolling too much (esp. on mobile), which negates the whole purpose of using an accordion. For very long panels, using links to separate pages is preferred.",
11 | "spans": []
12 | },
13 | {
14 | "type": "paragraph",
15 | "text": "If you use accordions to replace page steps such as checkout form steps, make sure you stop the default browser BACK button behavior so that the BACK button would move up a step in the accordion, because this is the behavior that most users expect, as tests show.",
16 | "spans": []
17 | }
18 | ]
19 | },
20 | {
21 | "title": [
22 | { "type": "heading3", "text": "What's a headless CMS?", "spans": [] }
23 | ],
24 | "text": [
25 | {
26 | "type": "paragraph",
27 | "text": "The default markup is enhanced using JavaScript so that the buttons are added to the headings. The buttons should NOT be in the headings by default. The markup should always consider what the component or content would look like if no Javascript is enabled and therefore the accordion is not functional. The content is most likely going to be all visible — either as a series of panels with headings or a definition list, depending on the type of content.",
28 | "spans": []
29 | }
30 | ]
31 | },
32 | {
33 | "title": [
34 | {
35 | "type": "heading3",
36 | "text": "What so special about you?",
37 | "spans": []
38 | }
39 | ],
40 | "text": [
41 | {
42 | "type": "paragraph",
43 | "text": "The buttons need to specify which panels they control usingaria-controls, and they need to reflect the state of the panel (collapsed/expanded) usingaria-expanded=\"true/false\". The panel itself also should havearia-hiddenset to true or false if it is hidden or shown, respectively.",
44 | "spans": []
45 | },
46 | {
47 | "type": "paragraph",
48 | "text": "The accordion needs to be able to trap keyboard focus when the user is IN the accordion and uses the arrow keys to traverse the items in it. Tabbing out of the accordion must be enabled.",
49 | "spans": []
50 | }
51 | ]
52 | },
53 | {
54 | "title": [
55 | { "type": "heading3", "text": "Are you that different?", "spans": [] }
56 | ],
57 | "text": [
58 | {
59 | "type": "paragraph",
60 | "text": "You can allow one or more panels to be always collapsed or expanded. You can also set a panel to be open by default (e.g. the first one). All this functionality can be tied to the markup using data attributes as flags.",
61 | "spans": []
62 | }
63 | ]
64 | }
65 | ],
66 | "primary": {
67 | "eyebrow_headline": [{ "type": "paragraph", "text": "FAQ", "spans": [] }],
68 | "title": [
69 | { "type": "heading2", "text": "Answers to common questions", "spans": [] }
70 | ],
71 | "description": [
72 | {
73 | "type": "paragraph",
74 | "text": "Learn about Prismic by reading questions and answers. It’s almost like talking to a human – and maybe even better.",
75 | "spans": []
76 | }
77 | ],
78 | "optional_image": {
79 | "dimensions": { "width": 2048, "height": 1536 },
80 | "alt": null,
81 | "copyright": null,
82 | "url": "https://images.prismic.io/slicesexamples/b4aeb1e2-e9c1-43ab-82ab-0d0519f9c3cd_float.png?auto=compress,format"
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/slices/FaqSection/mock.json:
--------------------------------------------------------------------------------
1 | {
2 | "slice_type": "faq_section",
3 | "slice_label": null,
4 | "items": [
5 | {
6 | "title": [{ "type": "heading3", "text": "Test tracking", "spans": [] }],
7 | "text": [
8 | {
9 | "type": "paragraph",
10 | "text": "Only use them when the content is appropriate. If the panels are too long, the user will end up scrolling too much (esp. on mobile), which negates the whole purpose of using an accordion. For very long panels, using links to separate pages is preferred.",
11 | "spans": []
12 | },
13 | {
14 | "type": "paragraph",
15 | "text": "If you use accordions to replace page steps such as checkout form steps, make sure you stop the default browser BACK button behavior so that the BACK button would move up a step in the accordion, because this is the behavior that most users expect, as tests show.",
16 | "spans": []
17 | }
18 | ]
19 | },
20 | {
21 | "title": [
22 | { "type": "heading3", "text": "What's a headless CMS?", "spans": [] }
23 | ],
24 | "text": [
25 | {
26 | "type": "paragraph",
27 | "text": "The default markup is enhanced using JavaScript so that the buttons are added to the headings. The buttons should NOT be in the headings by default. The markup should always consider what the component or content would look like if no Javascript is enabled and therefore the accordion is not functional. The content is most likely going to be all visible — either as a series of panels with headings or a definition list, depending on the type of content.",
28 | "spans": []
29 | }
30 | ]
31 | },
32 | {
33 | "title": [
34 | {
35 | "type": "heading3",
36 | "text": "What so special about you?",
37 | "spans": []
38 | }
39 | ],
40 | "text": [
41 | {
42 | "type": "paragraph",
43 | "text": "The buttons need to specify which panels they control usingaria-controls, and they need to reflect the state of the panel (collapsed/expanded) usingaria-expanded=\"true/false\". The panel itself also should havearia-hiddenset to true or false if it is hidden or shown, respectively.",
44 | "spans": []
45 | },
46 | {
47 | "type": "paragraph",
48 | "text": "The accordion needs to be able to trap keyboard focus when the user is IN the accordion and uses the arrow keys to traverse the items in it. Tabbing out of the accordion must be enabled.",
49 | "spans": []
50 | }
51 | ]
52 | },
53 | {
54 | "title": [
55 | { "type": "heading3", "text": "Are you that different?", "spans": [] }
56 | ],
57 | "text": [
58 | {
59 | "type": "paragraph",
60 | "text": "You can allow one or more panels to be always collapsed or expanded. You can also set a panel to be open by default (e.g. the first one). All this functionality can be tied to the markup using data attributes as flags.",
61 | "spans": []
62 | }
63 | ]
64 | }
65 | ],
66 | "primary": {
67 | "eyebrow_headline": [{ "type": "paragraph", "text": "FAQ", "spans": [] }],
68 | "title": [
69 | { "type": "heading1", "text": "Answers to common questions", "spans": [] }
70 | ],
71 | "description": [
72 | {
73 | "type": "paragraph",
74 | "text": "Learn about Prismic by reading questions and answers. It’s almost like talking to a human – and maybe even better.",
75 | "spans": []
76 | }
77 | ],
78 | "optional_image": {
79 | "dimensions": { "width": 2048, "height": 1536 },
80 | "alt": null,
81 | "copyright": null,
82 | "url": "https://images.prismic.io/slicesexamples/b4aeb1e2-e9c1-43ab-82ab-0d0519f9c3cd_float.png?auto=compress,format"
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/slices/AlternateGrid/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { RichText } from 'prismic-reactjs'
4 | import { Box, useThemeUI } from 'theme-ui'
5 | import {
6 | Button,
7 | Desc,
8 | Eyebrow,
9 | Grid,
10 | Head,
11 | TextBlock,
12 | Title,
13 | Slice,
14 | Wrap,
15 | } from '../../components'
16 | import { structuredTextPropTypes } from '../../utils/prop-types'
17 |
18 | const AlternateGrid = ({ slice }) => {
19 | const { items, primary } = slice
20 | const { theme } = useThemeUI()
21 | const defaultColumnSizes = ['40% 60%', '60% 40%']
22 | const imageOrder = primary.image_side === 'left' ? 0 : 1
23 | const columnValues = theme.container.grid.spans
24 | ? theme.container.grid.spans
25 | : defaultColumnSizes[imageOrder]
26 |
27 | const columns = primary.optional_image ? columnValues : '1'
28 | return (
29 |
30 |
31 |
32 | {primary.optional_image && (
33 |
38 | )}
39 |
40 |
45 |
46 | {primary.eyebrow_headline && (
47 |
48 |
49 |
50 | )}
51 | {primary.title && (
52 |
58 |
59 |
60 | )}
61 | {primary.description && (
62 |
63 |
64 |
65 | )}
66 |
67 |
68 |
74 | {items.map(
75 | ({ description, title, optional_icon: optionalIcon }) => {
76 | return (
77 |
82 | )
83 | }
84 | )}
85 | {primary.call_to_action &&
86 | primary.call_to_action.map(({ text, url, color }) => {
87 | return
88 | })}
89 |
90 |
91 |
92 |
93 |
94 | )
95 | }
96 |
97 | AlternateGrid.propTypes = {
98 | slice: PropTypes.shape({
99 | primary: PropTypes.shape({
100 | title: structuredTextPropTypes,
101 | description: structuredTextPropTypes,
102 | eyebrow_headline: structuredTextPropTypes,
103 | optional_image: PropTypes.shape({
104 | url: PropTypes.string,
105 | }),
106 | call_to_action: PropTypes.arrayOf(
107 | PropTypes.shape({
108 | text: PropTypes.string,
109 | url: PropTypes.string,
110 | color: PropTypes.string,
111 | })
112 | ),
113 | image_side: PropTypes.string,
114 | }).isRequired,
115 | items: PropTypes.array.isRequired,
116 | }).isRequired,
117 | }
118 |
119 | export default AlternateGrid
120 |
--------------------------------------------------------------------------------
/src/components/GridLayout/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Box } from 'theme-ui'
3 |
4 | import GridLayout from '.'
5 |
6 | export default {
7 | title: 'Components/GridLayout',
8 | component: GridLayout,
9 | }
10 |
11 | const styles = { '& *': { p: 'small' } }
12 |
13 | export const Default = () => (
14 |
15 |
16 |
17 | Number of columns not specified
18 |
19 | Number of columns not specified
20 |
21 | Number of columns not specified
22 |
23 | Number of columns not specified
24 |
25 | Number of columns not specified
26 |
27 | Number of columns not specified
28 |
29 | Number of columns not specified
30 |
31 | Number of columns not specified
32 |
33 | Number of columns not specified
34 |
35 | Number of columns not specified
36 |
37 | Number of columns not specified
38 |
39 | Number of columns not specified
40 |
41 |
42 | )
43 |
44 | export const Columns = () => (
45 | <>
46 |
47 |
48 | 1/2
49 | 1/2
50 | 1/2
51 | 1/2
52 |
53 |
54 |
55 |
56 | 1/3
57 | 2/3
58 |
59 |
60 |
61 |
62 | 1/3
63 | 1/3
64 | 1/3
65 |
66 |
67 |
68 |
69 | 1/4
70 | 3/4
71 |
72 |
73 |
74 |
75 | 1/4
76 | 1/4
77 | 1/4
78 | 1/4
79 |
80 |
81 | >
82 | )
83 |
84 | export const Width = () => (
85 | <>
86 |
87 |
88 | width=200px
89 | width=200px
90 | width=200px
91 | width=200px
92 | width=200px
93 | width=200px
94 | width=200px
95 | width=200px
96 | width=200px
97 | width=200px
98 | width=200px
99 | width=200px
100 |
101 |
102 | >
103 | )
104 |
--------------------------------------------------------------------------------
/src/components/Icon/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Box } from 'theme-ui'
4 |
5 | const Icons = {
6 | feature: `
7 | `,
23 | notIncluded: `
24 | `,
40 | play: `
41 | `,
57 | playBlack: `
58 | `,
74 | arrowDown: (props) => (
75 |
88 | ),
89 | arrowRight: (props) => (
90 |
107 | ),
108 | arrowLeft: (props) => (
109 |
126 | ),
127 | }
128 |
129 | const Icon = React.forwardRef(({ name, variant, ...props }, ref) => {
130 | return (
131 |
138 | )
139 | })
140 |
141 | Icon.encode = (name) => `data:image/svg+xml; utf8, ${Icons[name]}`
142 |
143 | Icon.propTypes = {
144 | name: PropTypes.string.isRequired,
145 | variant: PropTypes.string,
146 | }
147 |
148 | Icon.defaultProps = {
149 | variant: 'default',
150 | }
151 |
152 | export default Icon
153 |
--------------------------------------------------------------------------------
/src/slices/TestimonialsSlider/mock.json:
--------------------------------------------------------------------------------
1 | {
2 | "slice_type": "testimonials_slider",
3 | "slice_label": null,
4 | "items": [
5 | {
6 | "image": {
7 | "dimensions": {
8 | "width": 400,
9 | "height": 400
10 | },
11 | "alt": "Image 1",
12 | "copyright": null,
13 | "url": "https://images.prismic.io/slicesexamples/342cc816-3eeb-403c-a63b-79a678921258_OTg3ODQuanBn.jpg?auto=compress,format&rect=0,0,512,512&w=400&h=400"
14 | },
15 | "testimonial": [
16 | {
17 | "type": "paragraph",
18 | "text": "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words",
19 | "spans": []
20 | }
21 | ],
22 | "person": "John Doe",
23 | "title": "Software eng. at Prismic"
24 | },
25 | {
26 | "image": {
27 | "dimensions": {
28 | "width": 400,
29 | "height": 400
30 | },
31 | "alt": "Image 2",
32 | "copyright": null,
33 | "url": "https://images.prismic.io/slicesexamples/b3f9c02d-40f9-41b0-b0b7-4b05bcad0556_Mjk0MDIuanBn.jpg?auto=compress,format&rect=0,0,512,512&w=400&h=400"
34 | },
35 | "testimonial": [
36 | {
37 | "type": "paragraph",
38 | "text": "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words",
39 | "spans": []
40 | }
41 | ],
42 | "person": "Janine Doe",
43 | "title": "Software eng. at Prismic"
44 | },
45 | {
46 | "image": {
47 | "dimensions": {
48 | "width": 400,
49 | "height": 400
50 | },
51 | "alt": "Image 3",
52 | "copyright": null,
53 | "url": "https://images.prismic.io/slicesexamples/e273a2ed-45c1-4c61-8183-c7ff6fea8445_XzA4MDY2NzIuanBn.jpg?auto=compress,format&rect=0,0,512,512&w=400&h=400"
54 | },
55 | "testimonial": [
56 | {
57 | "type": "paragraph",
58 | "text": "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words",
59 | "spans": []
60 | }
61 | ],
62 | "person": "Josiane Doe",
63 | "title": "Software eng. at Prismic"
64 | },
65 | {
66 | "image": {
67 | "dimensions": {
68 | "width": 400,
69 | "height": 400
70 | },
71 | "alt": "Image 4",
72 | "copyright": null,
73 | "url": "https://images.prismic.io/slicesexamples/883af32f-539a-415f-9429-241195543d1b_OTk2NjQuanBn.jpg?auto=compress,format&rect=0,0,512,512&w=400&h=400"
74 | },
75 | "testimonial": [
76 | {
77 | "type": "paragraph",
78 | "text": "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words",
79 | "spans": []
80 | }
81 | ],
82 | "person": "Jasmine Doe",
83 | "title": "Software eng. at Prismic"
84 | }
85 | ],
86 | "primary": {
87 | "eyebrow_headline": [
88 | {
89 | "type": "paragraph",
90 | "text": "The Carousel",
91 | "spans": []
92 | }
93 | ],
94 | "title": [
95 | {
96 | "type": "heading1",
97 | "text": "It’s more than a budget manager",
98 | "spans": []
99 | }
100 | ],
101 | "description": [
102 | {
103 | "type": "paragraph",
104 | "text": "This carousel moves by one card at a time when the next and previous arrows are clicked",
105 | "spans": []
106 | }
107 | ]
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/slices/AlternateGrid/mock.json:
--------------------------------------------------------------------------------
1 | {
2 | "slice_type": "alternate_grid",
3 | "slice_label": null,
4 | "items": [
5 | {
6 | "optional_icon": {
7 | "dimensions": {
8 | "width": 2048,
9 | "height": 1536
10 | },
11 | "alt": null,
12 | "copyright": null,
13 | "url": "https://images.prismic.io/repoz/0b72eb6f-1824-4891-914a-c43fdee5b893_running.png?auto=compress,format"
14 | },
15 | "title": [
16 | {
17 | "type": "heading3",
18 | "text": "Integrate with the SliceZone",
19 | "spans": []
20 | }
21 | ],
22 | "description": [
23 | {
24 | "type": "paragraph",
25 | "text": "This component has been matched by the SliceZone. Its model has been fetched from vue-essential-slices.",
26 | "spans": []
27 | }
28 | ]
29 | },
30 | {
31 | "optional_icon": {
32 | "dimensions": {
33 | "width": 2048,
34 | "height": 1536
35 | },
36 | "alt": null,
37 | "copyright": null,
38 | "url": "https://images.prismic.io/repoz/b9b0d289-a1df-4067-a8b8-593bc3752c20_dog-jump.png?auto=compress,format"
39 | },
40 | "title": [
41 | {
42 | "type": "heading3",
43 | "text": "Add icons if required",
44 | "spans": []
45 | }
46 | ],
47 | "description": [
48 | {
49 | "type": "paragraph",
50 | "text": "This component has been matched by the SliceZone. Its model has been fetched from vue-essential-slices.",
51 | "spans": []
52 | }
53 | ]
54 | },
55 | {
56 | "optional_icon": {
57 | "dimensions": {
58 | "width": 2048,
59 | "height": 1536
60 | },
61 | "alt": null,
62 | "copyright": null,
63 | "url": "https://images.prismic.io/repoz/60377285-4f61-4500-b342-224f4715ec35_moshing.png?auto=compress,format"
64 | },
65 | "title": [
66 | {
67 | "type": "heading3",
68 | "text": "Add a custom theme",
69 | "spans": []
70 | }
71 | ],
72 | "description": [
73 | {
74 | "type": "paragraph",
75 | "text": "This component has been matched by the SliceZone. Its model has been fetched from vue-essential-slices.",
76 | "spans": []
77 | }
78 | ]
79 | },
80 | {
81 | "optional_icon": {
82 | "dimensions": {
83 | "width": 2048,
84 | "height": 1536
85 | },
86 | "alt": null,
87 | "copyright": null,
88 | "url": "https://images.prismic.io/repoz/c7b4408a-84f5-42f2-bd10-39785b66fbb1_laying.png?auto=compress,format"
89 | },
90 | "title": [
91 | {
92 | "type": "heading3",
93 | "text": "Create your own",
94 | "spans": []
95 | }
96 | ],
97 | "description": [
98 | {
99 | "type": "paragraph",
100 | "text": "This component has been matched by the SliceZone. Its model has been fetched from vue-essential-slices.",
101 | "spans": []
102 | }
103 | ]
104 | }
105 | ],
106 | "primary": {
107 | "eyebrow_headline": [
108 | {
109 | "type": "paragraph",
110 | "text": "Eyebrow",
111 | "spans": []
112 | }
113 | ],
114 | "title": [
115 | {
116 | "type": "heading1",
117 | "text": "Alternate like a star",
118 | "spans": []
119 | }
120 | ],
121 | "description": [
122 | {
123 | "type": "paragraph",
124 | "text": "A predesigned AlternateGrid component, that you can use to list your skills or features of a product.",
125 | "spans": []
126 | }
127 | ],
128 | "optional_image": {
129 | "dimensions": {
130 | "width": 1114,
131 | "height": 1522
132 | },
133 | "alt": null,
134 | "copyright": null,
135 | "url": "https://images.prismic.io/repoz/9aea6f86-1695-4ee9-9d34-335e020ed007_selfie2.png?auto=compress,format"
136 | },
137 | "image_side": "left",
138 | "call_to_action":[
139 | {
140 | "text":"Call to action",
141 | "url": "#",
142 | "color": "secondary"
143 | },
144 | {
145 | "text":"Call to action",
146 | "url": "#",
147 | "color": "light"
148 | }
149 | ]
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/slices/PricingTable/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable camelcase */
2 | import React from 'react'
3 | import PropTypes from 'prop-types'
4 | import { Box } from 'theme-ui'
5 | import { RichText } from 'prismic-reactjs'
6 | import uuid from 'react-uuid'
7 |
8 | import { Button, Wrap, Desc, Head, Slice } from '../../components'
9 |
10 | import { table, tableOption, features } from './styles'
11 |
12 | const featuresSerializer = (type, elementProps, _, children) => {
13 | if (type === 'list-item') {
14 | const isNotIncluded =
15 | elementProps.spans &&
16 | elementProps.spans.find((e) => e && e.data.label === 'not-included')
17 | const className = `ps-pricing-table__option__feature${
18 | isNotIncluded ? ' not-included' : ''
19 | }`
20 | return `${elementProps.text}`
21 | }
22 | if (type === 'group-list-item') {
23 | return (
24 |
28 | ${children.join('')}
29 | `,
30 | }}
31 | />
32 | )
33 | }
34 | return null
35 | }
36 |
37 | const PricingTable = ({
38 | slice: { primary, items },
39 | linkResolver, // linkResolver is passed by SliceZone
40 | }) => {
41 | const { eyebrow_headline, title, description } = primary
42 |
43 | return (
44 |
45 |
46 |
47 |
48 | {eyebrow_headline && (
49 |
57 | {RichText.asText(eyebrow_headline)}
58 |
59 | )}
60 |
61 |
62 | {description && (
63 |
64 |
65 |
66 | )}
67 |
68 |
69 |
70 | {items &&
71 | items.map((item) => (
72 |
73 |
74 |
75 | {RichText.asText(item.plan_title)}
76 |
81 | {RichText.asText(item.price_option)}
82 |
83 |
84 |
85 |
89 |
90 | {item.call_to_action &&
91 | item.call_to_action_text &&
92 | item.call_to_action_text.length && (
93 |
94 |
103 |
104 | )}
105 |
106 |
107 | ))}
108 |
109 |
110 |
111 |
112 | )
113 | }
114 |
115 | PricingTable.propTypes = {
116 | slice: PropTypes.shape({
117 | primary: PropTypes.shape({
118 | eyebrow_headline: PropTypes.array,
119 | title: PropTypes.array,
120 | description: PropTypes.array,
121 | }),
122 | items: PropTypes.array,
123 | }),
124 | linkResolver: PropTypes.func,
125 | }
126 |
127 | PricingTable.defaultProps = {
128 | slice: {
129 | primary: {
130 | eyebrow_headline: null,
131 | title: null,
132 | description: null,
133 | },
134 | items: [],
135 | },
136 | linkResolver: null,
137 | }
138 |
139 | export default PricingTable
140 |
--------------------------------------------------------------------------------
/src/components/Slider/styles.js:
--------------------------------------------------------------------------------
1 | export const arrowStyles = {
2 | cursor: 'pointer',
3 | borderRadius: '50%',
4 | display: 'block',
5 | fontSize: 0,
6 | height: '2.75rem',
7 | lineHeight: 0,
8 | outline: 'none',
9 | padding: '0.5rem',
10 | position: 'absolute',
11 | bottom: ['-70px', null, null, '50%'],
12 | transform: [null, null, null, 'translate(0, 50%)'],
13 | width: '2.75rem',
14 | }
15 |
16 | export const dotContainerStyles = {
17 | position: ' absolute',
18 | bottom: -40,
19 | display: 'block',
20 | width: '100%',
21 | p: 0,
22 | m: 0,
23 | listStyle: 'none',
24 | textAlign: 'center',
25 |
26 | ul: {
27 | m: 0,
28 | p: 0,
29 | },
30 |
31 | li: {
32 | position: 'relative',
33 | display: 'inline-block',
34 |
35 | m: 0,
36 | p: 0,
37 |
38 | cursor: 'pointer',
39 |
40 | '&.slick-active :before': {
41 | bg: 'primaryDark',
42 | },
43 | },
44 | }
45 |
46 | export const dotStyles = {
47 | bg: 'transparent',
48 | borderRadius: '50%',
49 | border: '2px dotted transparent',
50 | cursor: 'pointer',
51 | display: 'block',
52 | height: 40,
53 | fontSize: 0,
54 | outline: 'none',
55 | textAlign: 'center',
56 | width: 40,
57 |
58 | ':focus': {
59 | borderColor: 'primaryDark',
60 | },
61 |
62 | ':before': {
63 | bg: 'dark',
64 | width: 14,
65 | height: 14,
66 | content: '""',
67 | textAlign: 'center',
68 | display: 'inline-block',
69 | borderRadius: '50%',
70 |
71 | '-webkit-font-smoothing': 'antialiased',
72 | '-moz-osx-font-smoothing': 'grayscale',
73 | },
74 |
75 | span: {
76 | opacity: 0,
77 | pointerEvents: 'none',
78 | },
79 |
80 | '&:hover span': {
81 | opacity: 1,
82 | },
83 | }
84 |
85 | export const dotLabelStyles = {
86 | bottom: -35,
87 | bg: 'secondary',
88 | borderRadius: 4,
89 | fontWeight: 'lean',
90 | fontSize: 'base',
91 | transition: 'opacity 0.2s linear',
92 | p: 'xsmall',
93 | position: 'absolute',
94 | transform: 'translateX(-50%)',
95 | width: 215,
96 | left: 20,
97 |
98 | '&::after': {
99 | borderLeft: '1rem solid transparent',
100 | borderRight: '1rem solid transparent',
101 | borderBottom: '1rem solid',
102 | borderBottomColor: 'secondary',
103 | content: '""',
104 | left: '50%',
105 | marginLeft: '-1rem',
106 | position: 'absolute',
107 | top: '-0.5rem',
108 | height: 0,
109 | width: 0,
110 | },
111 | }
112 |
113 | export const slickStyles = {
114 | '.slick-slider': {
115 | position: 'relative',
116 | display: 'block',
117 | boxSizing: 'border-box',
118 |
119 | WebkitUserSelect: 'none',
120 | MozUserSelect: 'none',
121 | msUserSelect: 'none',
122 | userSelect: 'none',
123 |
124 | WebkitTouchCallout: 'none',
125 | KhtmlUserSelect: 'none',
126 | msTouchAction: 'pan-y',
127 | touchAction: 'pan-y',
128 | WebkitTapHighlightColor: 'transparent',
129 | },
130 |
131 | '.slick-list': {
132 | position: 'relative',
133 | display: 'block',
134 | overflow: 'hidden',
135 | margin: 0,
136 | padding: 0,
137 | },
138 |
139 | '.slick-list:focus': {
140 | outline: 'none',
141 | },
142 |
143 | '.slick-list.dragging': {
144 | cursor: 'pointer',
145 | },
146 |
147 | '.slick-slider .slick-track, .slick-slider .slick-list': {
148 | WebkitTransform: 'translate3d(0, 0, 0)',
149 | MozTransform: 'translate3d(0, 0, 0)',
150 | msTransform: 'translate3d(0, 0, 0)',
151 | OTransform: 'translate3d(0, 0, 0)',
152 | transform: 'translate3d(0, 0, 0)',
153 | },
154 |
155 | '.slick-track': {
156 | position: 'relative',
157 | top: 0,
158 | left: 0,
159 | display: 'block',
160 | mx: 'auto',
161 | },
162 |
163 | '.slick-track:before, .slick-track:after': {
164 | display: 'table',
165 | content: '""',
166 | },
167 |
168 | '.slick-track:after': {
169 | clear: 'both',
170 | },
171 |
172 | '.slick-loading .slick-track': {
173 | visibility: 'hidden',
174 | },
175 |
176 | '.slick-slide': {
177 | display: 'none',
178 | float: 'left',
179 | height: '100%',
180 | minHeight: 1,
181 | },
182 |
183 | "[dir='rtl'] .slick-slide": {
184 | float: 'right',
185 | },
186 |
187 | '.slick-slide img': {
188 | display: 'block',
189 | },
190 |
191 | '.slick-slide.slick-loading img': {
192 | display: 'none',
193 | },
194 |
195 | '.slick-slide.dragging img': {
196 | pointerEvents: 'none',
197 | },
198 |
199 | '.slick-initialized .slick-slide': {
200 | display: 'block',
201 | },
202 |
203 | '.slick-loading .slick-slide': {
204 | visibility: 'hidden',
205 | },
206 |
207 | '.slick-vertical .slick-slide': {
208 | display: 'block',
209 | height: 'auto',
210 | border: '1px solid transparent',
211 | },
212 |
213 | '.slick-arrow.slick-hidden': {
214 | display: 'none',
215 | },
216 |
217 | '.slick-loading .slick-list': {
218 | background: '#fff',
219 | },
220 |
221 | '.slick-dotted.slick-slider': {
222 | mb: 30,
223 | },
224 | }
225 |
--------------------------------------------------------------------------------
/src/slices/PricingTable/mock.json:
--------------------------------------------------------------------------------
1 | {
2 | "slice_type": "pricing_table",
3 | "slice_label": null,
4 | "items": [
5 | {
6 | "plan_title": [
7 | {
8 | "type": "paragraph",
9 | "text": "Small",
10 | "spans": []
11 | }
12 | ],
13 | "price_option": [
14 | {
15 | "type": "paragraph",
16 | "text": "Free",
17 | "spans": []
18 | }
19 | ],
20 | "features": [
21 | {
22 | "type": "list-item",
23 | "text": "Figma and Sketch Files",
24 | "spans": []
25 | },
26 | {
27 | "type": "list-item",
28 | "text": "Auto-Updatable Styles",
29 | "spans": []
30 | },
31 | {
32 | "type": "list-item",
33 | "text": "Base Elements",
34 | "spans": []
35 | },
36 | {
37 | "type": "list-item",
38 | "text": "Page Layouts",
39 | "spans": [
40 | {
41 | "start": 0,
42 | "end": 12,
43 | "type": "label",
44 | "data": {
45 | "label": "not-included"
46 | }
47 | }
48 | ]
49 | },
50 | {
51 | "type": "list-item",
52 | "text": "24/7 Free Support",
53 | "spans": [
54 | {
55 | "start": 0,
56 | "end": 17,
57 | "type": "label",
58 | "data": {
59 | "label": "not-included"
60 | }
61 | }
62 | ]
63 | }
64 | ],
65 | "call_to_action": {
66 | "link_type": "Web",
67 | "url": "https://prismic.io",
68 | "target": "_blank"
69 | },
70 | "call_to_action_text": [
71 | {
72 | "type": "paragraph",
73 | "text": "Start your free trial",
74 | "spans": []
75 | }
76 | ]
77 | },
78 | {
79 | "plan_title": [
80 | {
81 | "type": "paragraph",
82 | "text": "Medium",
83 | "spans": []
84 | }
85 | ],
86 | "price_option": [
87 | {
88 | "type": "paragraph",
89 | "text": "From $18",
90 | "spans": []
91 | }
92 | ],
93 | "features": [
94 | {
95 | "type": "list-item",
96 | "text": "Figma and Sketch Files",
97 | "spans": []
98 | },
99 | {
100 | "type": "list-item",
101 | "text": "Auto-Updatable Styles",
102 | "spans": []
103 | },
104 | {
105 | "type": "list-item",
106 | "text": "Base Elements",
107 | "spans": []
108 | },
109 | {
110 | "type": "list-item",
111 | "text": "Page Layouts",
112 | "spans": []
113 | },
114 | {
115 | "type": "list-item",
116 | "text": "24/7 Free Support",
117 | "spans": [
118 | {
119 | "start": 0,
120 | "end": 17,
121 | "type": "label",
122 | "data": {
123 | "label": "not-included"
124 | }
125 | }
126 | ]
127 | }
128 | ],
129 | "call_to_action": {
130 | "link_type": "Any"
131 | },
132 | "call_to_action_text": [
133 | {
134 | "type": "paragraph",
135 | "text": "Call To Action",
136 | "spans": []
137 | }
138 | ]
139 | },
140 | {
141 | "plan_title": [
142 | {
143 | "type": "paragraph",
144 | "text": "Large",
145 | "spans": []
146 | }
147 | ],
148 | "price_option": [
149 | {
150 | "type": "paragraph",
151 | "text": "From $39",
152 | "spans": []
153 | }
154 | ],
155 | "features": [
156 | {
157 | "type": "list-item",
158 | "text": "Figma and Sketch Files",
159 | "spans": []
160 | },
161 | {
162 | "type": "list-item",
163 | "text": "Auto-Updatable Styles",
164 | "spans": []
165 | },
166 | {
167 | "type": "list-item",
168 | "text": "Base Elements",
169 | "spans": []
170 | },
171 | {
172 | "type": "list-item",
173 | "text": "Page Layouts",
174 | "spans": []
175 | },
176 | {
177 | "type": "list-item",
178 | "text": "24/7 Free Support",
179 | "spans": []
180 | }
181 | ],
182 | "call_to_action": {
183 | "link_type": "Web",
184 | "url": "https://prismic.io",
185 | "target": "_blank"
186 | },
187 | "call_to_action_text": [
188 | {
189 | "type": "paragraph",
190 | "text": "Contact us",
191 | "spans": []
192 | }
193 | ]
194 | }
195 | ],
196 | "primary": {
197 | "eyebrow_headline": [
198 | {
199 | "type": "paragraph",
200 | "text": "Pricing",
201 | "spans": []
202 | }
203 | ],
204 | "title": [
205 | {
206 | "type": "heading2",
207 | "text": "Choose a plan",
208 | "spans": []
209 | }
210 | ],
211 | "description": [
212 | {
213 | "type": "paragraph",
214 | "text": "Choose the plan that works for you and streamline your design process in an unlimited number of projects.",
215 | "spans": []
216 | }
217 | ]
218 | }
219 | }
220 |
--------------------------------------------------------------------------------
/src/theme.js:
--------------------------------------------------------------------------------
1 | const breakpoints = [
2 | `640px`, // 40rem
3 | `896px`, // 56rem
4 | `1280px`, // 80rem
5 | ]
6 |
7 | const baseColors = {
8 | primary: '#8592e0',
9 | primaryDark: '#16745f',
10 | primaryDarker: '#0d5e4c',
11 | secondary: '#f4f0ec',
12 | grey0: '#000000',
13 | grey20: '#333333',
14 | grey90: '#e6e6e6',
15 | grey53: '#878787',
16 | white: '#fff',
17 | }
18 |
19 | const headings = {
20 | level: {
21 | 1: {
22 | fontSize: [
23 | '1.625rem', // 26px
24 | '2rem', // 32px
25 | '2.5rem', // 40px
26 | '2.75rem', // 44px
27 | ],
28 | lineHeight: 1.3,
29 | },
30 | 4: {
31 | lineHeight: 1.2,
32 | },
33 | },
34 | }
35 |
36 | export default {
37 | background: {
38 | default: {
39 | bg: 'white',
40 | color: 'dark',
41 | },
42 | dark: {
43 | bg: 'dark',
44 | color: 'white',
45 | },
46 | secondary: {
47 | bg: 'secondary',
48 | color: 'dark',
49 | },
50 | },
51 | breakpoints,
52 | buttons: {
53 | default: {
54 | bg: 'gray',
55 | color: 'white',
56 | '&:hover': {
57 | backgroundColor: 'primaryDark',
58 | },
59 | },
60 | primary: {
61 | bg: 'primaryDark',
62 | color: 'white',
63 | '&:hover': {
64 | backgroundColor: 'primaryDarker',
65 | },
66 | },
67 | secondary: {
68 | bg: 'grey0',
69 | color: 'white',
70 | '&:hover': {
71 | backgroundColor: 'grey20',
72 | },
73 | },
74 | light: {
75 | bg: 'white',
76 | color: 'grey0',
77 | border: '1px solid',
78 | borderColor: 'grey0',
79 | '&:hover': {
80 | backgroundColor: 'grey90',
81 | },
82 | },
83 | },
84 | slider: {
85 | arrow: {
86 | background: 'transparent',
87 | border: '2px dotted transparent',
88 | color: 'dark',
89 | '&:focus': {
90 | borderColor: 'dark',
91 | },
92 | '&:hover': {
93 | color: 'border',
94 | },
95 | },
96 | },
97 | colors: {
98 | ...baseColors,
99 | textGrey: baseColors.grey53,
100 | transparent: 'transparent',
101 | dark: baseColors.grey0,
102 | text: baseColors.grey20,
103 | background: baseColors.white,
104 | body: baseColors.grey0,
105 | border: '#ccc',
106 | modes: {
107 | dark: {
108 | background: baseColors.grey0,
109 | text: baseColors.white,
110 | },
111 | },
112 | },
113 | container: {
114 | inner: {
115 | py: '8vw',
116 | px: '2rem',
117 | },
118 | grid: {
119 | width: '100%',
120 | },
121 | wrapper: {
122 | maxWidth: ['90%', null, null, '75%'],
123 | m: '0 auto',
124 | },
125 | eyebrow: {
126 | color: 'primary',
127 | },
128 | description: {
129 | mr: 'auto',
130 | ml: 'auto',
131 | mb: 'hPadding',
132 | maxWidth: '38rem',
133 | fontSize: ['kicker', null, null, 'lg'],
134 | fontWeight: 'lean',
135 | p: {
136 | lineHeight: 'desc',
137 | display: 'inline-block',
138 | },
139 | 'p:last-of-type': {
140 | mb: '0',
141 | },
142 | },
143 | },
144 | fonts: {
145 | body:
146 | 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif',
147 | heading: '"Inter", serif',
148 | monospace: 'Menlo, monospace',
149 | },
150 | fontSizes: {
151 | tiny: '.875rem',
152 | base: '0.9rem',
153 | kicker: '1.15rem',
154 | lg: '1.4rem',
155 | xl: '2.75rem',
156 | },
157 | fontWeights: {
158 | lean: '350',
159 | body: '400',
160 | normal: '500',
161 | heading: '600',
162 | },
163 |
164 | lineHeights: {
165 | heading: '1.25',
166 | button: '1.3',
167 | desc: '1.45',
168 | body: '1.625',
169 | },
170 | radii: {
171 | button: '4px',
172 | card: '8px',
173 | },
174 | sizes: {
175 | full: '100%',
176 | },
177 | space: {
178 | // Legacy vuejs scales.
179 | hPaddingHalf: '1rem',
180 | hPadding: '2rem',
181 | cPadding: '1.25rem',
182 | cMargin: '1rem',
183 |
184 | vMargin: '2.5rem',
185 | vMargin2x: '5rem',
186 |
187 | vSpace: '8vw',
188 |
189 | // New spaces scale.
190 | none: 0,
191 | xxsmall: '0.25rem', // 4px
192 | xsmall: '0.5rem', // 8px
193 | small: '1rem', // 16px
194 | medium: '2rem', // 32px
195 | large: '4rem', // 64px
196 | xlarge: '8rem', // 128px
197 | xxlarge: '16rem', // 256px
198 | xxxlarge: '32rem', // 512px
199 | },
200 | tabs: {
201 | default: {
202 | item: {
203 | '&::after': {
204 | height: 2,
205 | left: 0,
206 | },
207 | },
208 | },
209 | vertical: {
210 | display: [null, 'flex'],
211 | label: {
212 | pt: ['small', 0],
213 | display: 'block',
214 | ':focus': {
215 | outline: '3px solid currentColor',
216 | zIndex: '1',
217 | },
218 | },
219 | content: {
220 | width: '100%',
221 | },
222 | item: {
223 | width: '100%',
224 | mx: 0,
225 | pr: 'medium',
226 | borderTop: '1px solid',
227 | borderColor: 'grey90',
228 | ':last-of-type:not(.active)': {
229 | borderBottom: '1px solid',
230 | borderColor: 'grey90',
231 | },
232 | },
233 | },
234 | },
235 |
236 | // Default Components
237 |
238 | cards: {
239 | default: {
240 | bg: 'secondary',
241 | color: 'dark',
242 | },
243 | },
244 | styles: {
245 | root: {
246 | color: 'text',
247 | bg: 'background',
248 | fontSize: 'base',
249 | fontFamily: 'body',
250 | lineHeight: 'body',
251 | fontWeight: 'body',
252 | m: 0,
253 | p: 0,
254 | },
255 | 'h1, h2, h3, h4': {
256 | mt: 0,
257 | },
258 | h1: {
259 | ...headings.level[1],
260 | },
261 | p: {
262 | fontSize: 'body',
263 | mt: 0,
264 | mb: 0,
265 | },
266 | img: {
267 | maxWidth: '100%',
268 | },
269 | },
270 | text: {
271 | desc: {
272 | fontSize: 'lg',
273 | fontWeight: 'lean',
274 | lineHeight: 'desc',
275 | mx: 'auto',
276 | mt: 'vMargin',
277 | maxWidth: '42rem',
278 | },
279 | },
280 | }
281 |
--------------------------------------------------------------------------------
/src/slices/CardsCarousel/mock.json:
--------------------------------------------------------------------------------
1 | {
2 | "slice_type": "cards_carousel",
3 | "slice_label": "large_images",
4 | "items": [
5 | {
6 | "image": {
7 | "dimensions": {
8 | "width": 2048,
9 | "height": 1536
10 | },
11 | "alt": "Title 1",
12 | "copyright": null,
13 | "url": "https://images.prismic.io/slicesexamples/b4aeb1e2-e9c1-43ab-82ab-0d0519f9c3cd_float.png?auto=compress,format"
14 | },
15 | "title": [
16 | {
17 | "type": "heading3",
18 | "text": "Title 1",
19 | "spans": []
20 | }
21 | ],
22 | "content": [
23 | {
24 | "type": "paragraph",
25 | "text": "Content hello",
26 | "spans": []
27 | }
28 | ],
29 | "additional_info": [
30 | {
31 | "type": "paragraph",
32 | "text": "",
33 | "spans": []
34 | }
35 | ]
36 | },
37 | {
38 | "image": {
39 | "dimensions": {
40 | "width": 2048,
41 | "height": 1536
42 | },
43 | "alt": "Title 2",
44 | "copyright": null,
45 | "url": "https://images.prismic.io/slicesexamples/032aad44-343c-4ad6-b694-57bb9322ce88_clumsy.png?auto=compress,format"
46 | },
47 | "title": [
48 | {
49 | "type": "heading3",
50 | "text": "Title 2",
51 | "spans": []
52 | }
53 | ],
54 | "content": [
55 | {
56 | "type": "paragraph",
57 | "text": "lalala",
58 | "spans": []
59 | }
60 | ],
61 | "additional_info": []
62 | },
63 | {
64 | "image": {
65 | "dimensions": {
66 | "width": 2048,
67 | "height": 1536
68 | },
69 | "alt": "Title 3",
70 | "copyright": null,
71 | "url": "https://images.prismic.io/slicesexamples/7e9ead9f-accf-418b-9c5c-654f162d95ee_groovy.png?auto=compress,format"
72 | },
73 | "title": [
74 | {
75 | "type": "heading1",
76 | "text": "Title 3",
77 | "spans": []
78 | }
79 | ],
80 | "content": [
81 | {
82 | "type": "paragraph",
83 | "text": "content 1",
84 | "spans": []
85 | }
86 | ],
87 | "additional_info": []
88 | },
89 | {
90 | "image": {
91 | "dimensions": {
92 | "width": 2048,
93 | "height": 1536
94 | },
95 | "alt": "Title 4",
96 | "copyright": null,
97 | "url": "https://images.prismic.io/slicesexamples/f55cf641-b6d3-41f2-86f3-368a16b5decf_meditating.png?auto=compress,format"
98 | },
99 | "title": [
100 | {
101 | "type": "heading1",
102 | "text": "Title 4",
103 | "spans": []
104 | }
105 | ],
106 | "content": [
107 | {
108 | "type": "paragraph",
109 | "text": "Hello",
110 | "spans": []
111 | }
112 | ],
113 | "additional_info": []
114 | },
115 | {
116 | "image": {
117 | "dimensions": {
118 | "width": 2048,
119 | "height": 1536
120 | },
121 | "alt": "Title 5",
122 | "copyright": null,
123 | "url": "https://images.prismic.io/slicesexamples/bd0f784c-1039-44b8-8b73-f4978019d21b_dog-jump.png?auto=compress,format"
124 | },
125 | "title": [
126 | {
127 | "type": "heading1",
128 | "text": "Title 5",
129 | "spans": []
130 | }
131 | ],
132 | "content": [
133 | {
134 | "type": "paragraph",
135 | "text": "Hello",
136 | "spans": []
137 | }
138 | ],
139 | "additional_info": []
140 | },
141 | {
142 | "image": {
143 | "dimensions": {
144 | "width": 2048,
145 | "height": 1536
146 | },
147 | "alt": "Title 6",
148 | "copyright": null,
149 | "url": "https://images.prismic.io/slicesexamples/da71d741-4bf5-4b11-9b96-b97458c41ff0_dancing.png?auto=compress,format"
150 | },
151 | "title": [
152 | {
153 | "type": "heading1",
154 | "text": "Title 6",
155 | "spans": []
156 | }
157 | ],
158 | "content": [
159 | {
160 | "type": "paragraph",
161 | "text": "Saloud",
162 | "spans": []
163 | }
164 | ],
165 | "additional_info": [
166 | {
167 | "type": "paragraph",
168 | "text": "Additional info",
169 | "spans": []
170 | }
171 | ]
172 | },
173 | {
174 | "image": {
175 | "dimensions": {
176 | "width": 2048,
177 | "height": 1536
178 | },
179 | "alt": "Title 7",
180 | "copyright": null,
181 | "url": "https://images.prismic.io/slicesexamples/c2d957c2-cef3-4e55-ab52-4b5af1917f2f_ballet.png?auto=compress,format"
182 | },
183 | "title": [
184 | {
185 | "type": "heading3",
186 | "text": "Title 7",
187 | "spans": []
188 | }
189 | ],
190 | "content": [
191 | {
192 | "type": "paragraph",
193 | "text": "Bi1 ou cmt?",
194 | "spans": []
195 | }
196 | ],
197 | "additional_info": []
198 | },
199 | {
200 | "image": {
201 | "dimensions": {
202 | "width": 2048,
203 | "height": 1536
204 | },
205 | "alt": "Title 8",
206 | "copyright": null,
207 | "url": "https://images.prismic.io/slicesexamples/360d18f5-87f1-49f1-b749-17d7657e824a_chilling.png?auto=compress,format"
208 | },
209 | "title": [
210 | {
211 | "type": "heading3",
212 | "text": "Title 8",
213 | "spans": []
214 | }
215 | ],
216 | "content": [
217 | {
218 | "type": "paragraph",
219 | "text": "Some content again",
220 | "spans": []
221 | }
222 | ],
223 | "additional_info": []
224 | },
225 | {
226 | "image": {
227 | "dimensions": {
228 | "width": 2048,
229 | "height": 1536
230 | },
231 | "alt": "Title 9",
232 | "copyright": null,
233 | "url": "https://images.prismic.io/slicesexamples/c828478d-36eb-46b6-a96d-328c128059cb_ice-cream.png?auto=compress,format"
234 | },
235 | "title": [
236 | {
237 | "type": "heading3",
238 | "text": "Title 9",
239 | "spans": []
240 | }
241 | ],
242 | "content": [
243 | {
244 | "type": "paragraph",
245 | "text": "This is it!",
246 | "spans": []
247 | }
248 | ],
249 | "additional_info": []
250 | }
251 | ],
252 | "primary": {
253 | "eyebrow_headline": [
254 | {
255 | "type": "paragraph",
256 | "text": "The Carousel",
257 | "spans": []
258 | }
259 | ],
260 | "title": [
261 | {
262 | "type": "heading1",
263 | "text": "It’s more than a budget manager",
264 | "spans": []
265 | }
266 | ],
267 | "description": [
268 | {
269 | "type": "paragraph",
270 | "text": "This carousel moves by one card at a time when the next and previous arrows are clicked.",
271 | "spans": []
272 | }
273 | ]
274 | }
275 | }
276 |
--------------------------------------------------------------------------------
/sm.json:
--------------------------------------------------------------------------------
1 | {"libraryName":"React essentials","framework":"next","gitUrl":"//github.com/prismicio/essential-slices","pathToLibrary":"src","dependencies":["theme-ui"],"css":[],"devDependencies":[],"packageName":"essential-slices","package":{"name":"essential-slices","version":"1.0.3","description":"","source":"src/index.js","main":"dist/index.js","module":"dist/index.module.js","unpkg":"dist/index.umd.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1","storybook":"start-storybook -p 6006","build-storybook":"build-storybook","build":"microbundle --jsx React.createElement && yarn run bundle","bundle":"node ./node_modules/sm-commons/scripts/bundle","lint":"eslint .","lint:fix":"eslint . --fix","format":"prettier --write \"**/*.{js,jsx,json,md}\"","prepare":"husky install"},"eslintConfig":{"extends":"react-app"},"repository":{"type":"git","url":"git+https://github.com/prismicio/essential-slices.git"},"keywords":[],"author":"","license":"ISC","bugs":{"url":"https://github.com/prismicio/essential-slices/issues"},"homepage":"https://github.com/prismicio/essential-slices#readme","dependencies":{"react-slick":"^0.28.1","react-uuid":"^1.0.2"},"devDependencies":{"@babel/core":"^7.10.2","@storybook/addon-a11y":"^5.3.19","@storybook/addon-actions":"^5.3.19","@storybook/addon-docs":"^5.3.19","@storybook/addon-links":"^5.3.19","@storybook/addon-viewport":"^5.3.19","@storybook/addons":"^5.3.19","@storybook/react":"^5.3.19","babel-loader":"^8.1.0","eslint":"^7.1.0","eslint-config-airbnb":"^18.1.0","eslint-config-prettier":"^8.1.0","eslint-plugin-import":"^2.20.2","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.3","eslint-plugin-react":"^7.20.0","husky":"^6.0.0","lint-staged":"^10.2.8","microbundle":"^0.13.0","prettier":"^2.0.5","prismic-reactjs":"^1.3.1","prop-types":"^15.7.2","react":"^17.0.2","react-dom":"^17.0.2","sm-commons":"^0.0.23","storybook-addon-color-mode":"^1.2.5","theme-ui":"^0.6.2"},"peerDependencies":{"prismic-reactjs":"^1.3.1","prop-types":"^15.7.2","react":"^16.13.1 || 17","react-dom":"^16.13.1 || 17","theme-ui":"^0.6.2"}},"slices":{"alternate_grid":{"type":"Slice","fieldset":"AlternateGrid","description":"A predesigned Text Grid with Image left or right","icon":"wrap_text","display":"list","non-repeat":{"eyebrow_headline":{"type":"StructuredText","config":{"multi":"paragraph, preformatted, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item","allowTargetBlank":true,"label":"Eyebrow headline"}},"title":{"type":"StructuredText","config":{"single":"heading1, heading2, heading3, heading4, heading5, heading6","label":"title"}},"description":{"type":"StructuredText","config":{"multi":"paragraph, preformatted, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item","allowTargetBlank":true,"label":"description"}},"optional_image":{"type":"Image","config":{"constraint":{},"thumbnails":[],"label":"Optional image"}},"image_side":{"type":"Select","config":{"options":["left","right"],"default_value":"left","label":"Image side"}}},"repeat":{"optional_icon":{"type":"Image","config":{"constraint":{},"thumbnails":[],"label":"Optional Icon"}},"title":{"type":"StructuredText","config":{"single":"heading3","label":"title"}},"description":{"type":"StructuredText","config":{"multi":"paragraph, preformatted, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item","allowTargetBlank":true,"label":"description"}}}},"call_to_action":{"type":"Slice","fieldset":"Call to action","description":"A predesigned 'Call to action' for section for your site","icon":"notifications_active","display":"list","non-repeat":{"icon_image":{"type":"Image","config":{"constraint":{},"thumbnails":[],"label":"Icon Image"}},"title":{"type":"StructuredText","config":{"single":"heading1, heading2, heading3, heading4, heading5, heading6","label":"Title"}},"paragraph":{"type":"StructuredText","config":{"multi":"paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item","label":"Paragraph"}},"button_link":{"type":"Link","config":{"label":"Button Link"}},"button_label":{"type":"Text","config":{"label":"Button Label","placeholder":"Text for button"}}},"repeat":{}},"cards_carousel":{"type":"Slice","fieldset":"Cards Carousel","description":"A carousel with text + image cards","icon":"image","display":"list","non-repeat":{"eyebrow_headline":{"type":"StructuredText","config":{"multi":"paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item","allowTargetBlank":true,"label":"Eyebrow headline"}},"title":{"type":"StructuredText","config":{"single":"heading1, heading2, heading3, heading4, heading5, heading6","label":"Title"}},"description":{"type":"StructuredText","config":{"multi":"paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item","allowTargetBlank":true,"label":"Description"}}},"repeat":{"image":{"type":"Image","config":{"constraint":{"width":null,"height":null},"thumbnails":[],"label":"Image"}},"title":{"type":"StructuredText","config":{"single":"heading3","label":"Title"}},"content":{"type":"StructuredText","config":{"multi":"paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item","allowTargetBlank":true,"label":"Content"}},"additional_info":{"type":"StructuredText","config":{"multi":"paragraph, preformatted, heading3, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item","allowTargetBlank":true,"label":"Additional Info","placeholder":"eg. name of person in testimonial"}}}},"customer_logos":{"type":"Slice","fieldset":"Customer logos","description":"Display a list of your customers logos","icon":"person","display":"list","non-repeat":{"eyebrow_headline":{"type":"StructuredText","config":{"single":"heading2","label":"Eyebrow Headline","placeholder":"Trusted by"}},"call_to_action":{"type":"StructuredText","config":{"single":"paragraph","label":"Call To Action","placeholder":"View customer stories"}},"call_to_action_link":{"type":"Link","config":{"allowTargetBlank":true,"label":"Call to Action Link","placeholder":"Could be a signup link, or a link to customer stories"}}},"repeat":{"logo":{"type":"Image","config":{"constraint":{},"thumbnails":[],"label":"Logo"}},"link":{"type":"Link","config":{"allowTargetBlank":true,"label":"link","placeholder":"Could be a link to use case, press article, signup..."}}}},"faq_section":{"type":"Slice","fieldset":"FAQ","description":"List of common questions + answers","icon":"question_answer","display":"list","non-repeat":{"eyebrow_headline":{"type":"StructuredText","config":{"single":"paragraph","label":"Eyebrow headline","placeholder":"Reinforce your copy with a key-worded text, to be displayed before title"}},"title":{"type":"StructuredText","config":{"single":"heading2","label":"title","placeholder":"Title"}},"description":{"type":"StructuredText","config":{"multi":"paragraph, strong, em, hyperlink","allowTargetBlank":true,"label":"Description"}},"optional_image":{"type":"Image","config":{"constraint":{},"thumbnails":[],"label":"Optional image"}}},"repeat":{"title":{"type":"StructuredText","config":{"single":"heading3","label":"Title","placeholder":"Which browsers do you support?"}},"text":{"type":"StructuredText","config":{"multi":"paragraph, preformatted, strong, em, hyperlink, list-item, o-list-item","allowTargetBlank":true,"label":"Text","placeholder":"The answer to the question"}}}},"images_slider":{"type":"Slice","fieldset":"Images Slider","description":"A slider of full-width images + description","icon":"image","display":"list","non-repeat":{"eyebrow_headline":{"type":"StructuredText","config":{"multi":"paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item","allowTargetBlank":true,"label":"Eyebrow headline"}},"title":{"type":"StructuredText","config":{"single":"heading1, heading2, heading3, heading4, heading5, heading6","label":"Title"}},"description":{"type":"StructuredText","config":{"multi":"paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item","allowTargetBlank":true,"label":"Description"}}},"repeat":{"image":{"type":"Image","config":{"constraint":{"width":null,"height":null},"thumbnails":[],"label":"Full Width Image"}},"description":{"type":"StructuredText","config":{"multi":"paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item","allowTargetBlank":true,"label":"Description"}}}},"pricing_table":{"type":"Slice","fieldset":"Pricing Table","description":"Display a list of pricing plans","icon":"attach_money","display":"grid","non-repeat":{"eyebrow_headline":{"type":"StructuredText","config":{"single":"paragraph","label":"Eyebrow Headline","placeholder":"Pricing plans"}},"title":{"type":"StructuredText","config":{"single":"heading2","label":"Title","placeholder":"Choose the plan"}},"description":{"type":"StructuredText","config":{"multi":"paragraph, strong, em, hyperlink","allowTargetBlank":true,"label":"Description","placeholder":"Choose the version that works for you ..."}}},"repeat":{"plan_title":{"type":"StructuredText","config":{"single":"heading3","label":"Plan title","placeholder":"Simple, Gold, Premium..."}},"price_option":{"type":"StructuredText","config":{"multi":"heading4","label":"Price Option","placeholder":"Free, $19, Contact us..."}},"features":{"type":"StructuredText","config":{"multi":"list-item","label":"Features","placeholder":"A list of features using bullet list"}},"call_to_action":{"type":"Link","config":{"allowTargetBlank":true,"label":"Call To Action","placeholder":"Link to Signup / More info..."}},"call_to_action_text":{"type":"StructuredText","config":{"multi":"paragraph","label":"Call To Action Text","placeholder":"Start your free trial"}}}},"testimonials_slider":{"type":"Slice","fieldset":"Testimonials Slider","description":"A predesigned Slider with rich testimonial","icon":"notifications_active","display":"list","non-repeat":{"eyebrow_headline":{"type":"StructuredText","config":{"multi":"paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item","allowTargetBlank":true,"label":"Eyebrow headline"}},"title":{"type":"StructuredText","config":{"single":"heading1, heading2, heading3, heading4, heading5, heading6","label":"Title"}},"paragraph":{"type":"StructuredText","config":{"multi":"paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item","label":"Paragraph"}},"button_link":{"type":"Link","config":{"label":"Button Link"}},"button_label":{"type":"Text","config":{"label":"Button Label","placeholder":"Text for button"}}},"repeat":{"image":{"type":"Image","config":{"constraint":{},"thumbnails":[],"label":"image"}},"testimonial":{"type":"StructuredText","config":{"multi":"paragraph, preformatted, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item","label":"testimonial"}},"person":{"type":"Text","config":{"label":"person","placeholder":"Their full name"}},"title":{"type":"Text","config":{"label":"title","placeholder":"Their title"}}}},"video_highlights":{"type":"Slice","fieldset":"Video Highlights","description":"Highlights of your video channel","icon":"ondemand_video","display":"list","non-repeat":{"eyebrow_headline":{"type":"StructuredText","config":{"single":"paragraph","label":"Eyebrow headline","placeholder":"Reinforce your copy with a key-worded text, to be displayed before title"}},"title":{"type":"StructuredText","config":{"single":"heading2","label":"Title","placeholder":"Video Highlights"}},"description":{"type":"StructuredText","config":{"multi":"paragraph, strong, em, hyperlink","allowTargetBlank":true,"label":"Description","placeholder":"These are some awesome videos ..."}}},"repeat":{"video_title":{"type":"StructuredText","config":{"multi":"paragraph, strong","label":"Video title","placeholder":"My awesome video"}},"video_src":{"type":"Embed","config":{"label":"Video src"}}}}}}
--------------------------------------------------------------------------------