Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(UI): Add Hero component #1336

Merged
merged 15 commits into from
Jun 3, 2022
Merged
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()
hellofanny marked this conversation as resolved.
Show resolved Hide resolved
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