Skip to content

Commit

Permalink
feat(UI): Add Hero component (#1336)
Browse files Browse the repository at this point in the history
* Adds base code for Hero component

* Adds Hero components

* Adds base styles to example

* Updates styles

* Renames `HeroContent` to `HeroHeading`

* Updates styles

* Adds missing flie

* Fixes typo

* Removes `HeroLink` component

* Update packages/ui/src/organisms/Hero/Hero.test.tsx

Co-authored-by: Eduardo Formiga <eduardo.formiga@gmail.com>

* Update packages/ui/src/organisms/Hero/HeroHeading.tsx

Co-authored-by: Filipe W. Lima <filipe.lima@vtex.com.br>

* Removing children prop

Co-authored-by: Eduardo Formiga <eduardo.formiga@gmail.com>
Co-authored-by: Filipe W. Lima <filipe.lima@vtex.com.br>
  • Loading branch information
3 people authored Jun 3, 2022
1 parent fce6163 commit 767c0cd
Show file tree
Hide file tree
Showing 10 changed files with 300 additions and 0 deletions.
51 changes: 51 additions & 0 deletions packages/styles/src/organisms/hero.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
[data-store-hero] {
display: flex;
flex-direction: column;
width: 100%;
min-height: 20rem;
background-color: #001947;
color: #fff;
}

[data-hero-heading] {
display: flex;
flex-direction: column;
justify-content: center;
padding: 2rem;
}

[data-hero-heading] h1 {
font-size: 1.81rem;
font-weight: bold;
margin-bottom: 1.5rem;
}

[data-hero-heading] a {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
margin-top: 2rem;
min-width: 11.25rem;
width: fit-content;
color: #001947;
border: 1px solid #171a1c;
background-color: #fff;
}

[data-hero-image] {
height: 100%;
overflow: hidden;
}

@media only screen and (min-width: 769px) {
[data-store-hero] {
flex-direction: row-reverse;
}
[data-hero-heading] {
padding: 0 4rem;
}
[data-hero-heading] h1 {
font-size: 3rem;
}
}
1 change: 1 addition & 0 deletions packages/styles/src/organisms/index.css
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
@import './hero.css';
@import './out-of-stock.css';
7 changes: 7 additions & 0 deletions packages/ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,13 @@ export type {
OutOfStockTitleProps,
} from './organisms/OutOfStock'

export { default as Hero, HeroHeading, HeroImage } from './organisms/Hero'
export type {
HeroProps,
HeroHeadingProps,
HeroImageProps,
} from './organisms/Hero'

// Hooks
export { default as useSlider } from './hooks/useSlider'
export type {
Expand Down
60 changes: 60 additions & 0 deletions packages/ui/src/organisms/Hero/Hero.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { render } from '@testing-library/react'
import { axe } from 'jest-axe'
import React from 'react'

import Hero from './Hero'
import HeroImage from './HeroImage'
import HeroHeading from './HeroHeading'

const HeroTest = () => {
return (
<Hero data-custom-attribute>
<HeroImage>
<img
alt="Quest 2 Controller on a table"
src="https://storeframework.vtexassets.com/arquivos/ids/190897/Photo.jpg"
/>
</HeroImage>
<HeroHeading>
<h3>Get yo know our next release</h3>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
<a href="/">Shop Now</a>
</HeroHeading>
</Hero>
)
}

describe('Hero', () => {
describe('Data attributes', () => {
const { getByTestId } = render(<HeroTest />)

const hero = getByTestId('store-hero')
const heroImage = getByTestId('store-hero-image')
const heroHeading = getByTestId('store-hero-heading')

it('`Hero` component should have `data-store-hero` attribute', () => {
expect(hero).toHaveAttribute('data-store-hero')
})

it('`Hero` component should have custom data attribute `data-custom-attribute`', () => {
expect(hero).toHaveAttribute('data-custom-attribute')
})

it('`HeroImage` component should have `data-hero-image` attribute', () => {
expect(heroImage).toHaveAttribute('data-hero-image')
})

it('`HeroHeading` component should have `data-hero-heading` attribute', () => {
expect(heroHeading).toHaveAttribute('data-hero-heading')
})
})

describe('Accessibility', () => {
it('should have no violations', async () => {
const { getByTestId } = render(<HeroTest />)

expect(await axe(getByTestId('store-hero'))).toHaveNoViolations()
expect(await axe(getByTestId('store-hero'))).toHaveNoIncompletes()
})
})
})
23 changes: 23 additions & 0 deletions packages/ui/src/organisms/Hero/Hero.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React, { forwardRef } from 'react'
import type { HTMLAttributes } from 'react'

export interface HeroProps extends HTMLAttributes<HTMLDivElement> {
/**
* ID to find this component in testing tools (e.g.: cypress,
* testing-library, and jest).
*/
testId?: string
}

const Hero = forwardRef<HTMLDivElement, HeroProps>(function Hero(
{ testId = 'store-hero', children, ...otherProps },
ref
) {
return (
<article ref={ref} data-store-hero data-testid={testId} {...otherProps}>
{children}
</article>
)
})

export default Hero
24 changes: 24 additions & 0 deletions packages/ui/src/organisms/Hero/HeroHeading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { HTMLAttributes } from 'react'
import React, { forwardRef } from 'react'

export interface HeroHeadingProps extends HTMLAttributes<HTMLDivElement> {
/**
* ID to find this component in testing tools (e.g.: cypress, testing library, and jest).
*/
testId?: string
}

const HeroHeading = forwardRef<HTMLDivElement, HeroHeadingProps>(
function HeroHeading(
{ testId = 'store-hero-heading', children, ...otherProps },
ref
) {
return (
<header ref={ref} data-hero-heading data-testid={testId} {...otherProps}>
{children}
</header>
)
}
)

export default HeroHeading
22 changes: 22 additions & 0 deletions packages/ui/src/organisms/Hero/HeroImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { HTMLAttributes } from 'react'
import React, { forwardRef } from 'react'

export interface HeroImageProps extends HTMLAttributes<HTMLDivElement> {
/**
* ID to find this component in testing tools (e.g.: cypress, testing library, and jest).
*/
testId?: string
}

const HeroImage = forwardRef<HTMLDivElement, HeroImageProps>(function HeroImage(
{ testId = 'store-hero-image', children, ...otherProps },
ref
) {
return (
<div ref={ref} data-hero-image data-testid={testId} {...otherProps}>
{children}
</div>
)
})

export default HeroImage
8 changes: 8 additions & 0 deletions packages/ui/src/organisms/Hero/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export { default } from './Hero'
export type { HeroProps } from './Hero'

export { default as HeroImage } from './HeroImage'
export type { HeroImageProps } from './HeroImage'

export { default as HeroHeading } from './HeroHeading'
export type { HeroHeadingProps } from './HeroHeading'
48 changes: 48 additions & 0 deletions packages/ui/src/organisms/Hero/stories/Hero.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Canvas, Props, Story, ArgsTable } from '@storybook/addon-docs'

import Hero from '../Hero'
import HeroImage from '../HeroImage'
import HeroHeading from '../HeroHeading'

# Hero

<Canvas>
<Story id="organisms-hero--hero" />
</Canvas>

## Components

The `Hero` uses the [Compound Component](https://kentcdodds.com/blog/compound-components-with-react-hooks) pattern, its components are:

- `Hero`: the wrapper component.
- `HeroImage`: the component that wraps the image displayed in the component.
- `HeroHeading`: the component should have a title, description, and a call to action link.

## Props

All hero-related components support all attributes also supported by the `<div>` tag.

Besides those attributes, the following props are also supported:

### `Hero`

<ArgsTable of={Hero} />

### `HeroImage`

<ArgsTable of={HeroImage} />

### `HeroHeading`

<ArgsTable of={HeroHeading} />

## CSS Selectors

```css
[data-store-hero] {
}
[data-hero-image] {
}
[data-hero-heading] {
}
```
56 changes: 56 additions & 0 deletions packages/ui/src/organisms/Hero/stories/Hero.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import type { Story, Meta } from '@storybook/react'
import React from 'react'

import HeroComponent from '../Hero'
import HeroImage from '../HeroImage'
import HeroHeading from '../HeroHeading'
import type { HeroProps } from '../Hero'
import { Icon } from '../../..'
import mdx from './Hero.mdx'

const RightArrow = () => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 18 18"
>
<path
d="M10.6553 3.40717C10.3624 3.11428 9.88756 3.11428 9.59467 3.40717C9.30178 3.70006 9.30178 4.17494 9.59467 4.46783L13.3768 8.25H2.8125C2.39829 8.25 2.0625 8.58579 2.0625 9C2.0625 9.41421 2.39829 9.75 2.8125 9.75H13.3768L9.59467 13.5322C9.30178 13.8251 9.30178 14.2999 9.59467 14.5928C9.88756 14.8857 10.3624 14.8857 10.6553 14.5928L15.7178 9.53033C15.8643 9.38388 15.9375 9.19194 15.9375 9C15.9375 8.89831 15.9173 8.80134 15.8806 8.71291C15.844 8.62445 15.7897 8.54158 15.7178 8.46967L10.6553 3.40717Z"
fill="currentColor"
/>
</svg>
)

const HeroTemplate: Story<HeroProps> = ({ testId }) => (
<HeroComponent testId={testId}>
<HeroImage>
<img
alt="Quest 2 Controller on a table"
src="https://storeframework.vtexassets.com/arquivos/ids/190897/Photo.jpg"
/>
</HeroImage>
<HeroHeading>
<h1>New Products Available</h1>
<p>
At BaseStore you can shop the best tech of 2022. Enjoy and get 10% off
on your first purchase.
</p>
<a href="/">
See all <Icon component={<RightArrow />} />
</a>
</HeroHeading>
</HeroComponent>
)

export const Hero = HeroTemplate.bind({})
Hero.storyName = 'Hero'

export default {
title: 'Organisms/Hero',
parameters: {
docs: {
page: mdx,
},
},
} as Meta

1 comment on commit 767c0cd

@vercel
Copy link

@vercel vercel bot commented on 767c0cd Jun 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.