From 37eac86ede448ef68aef426f65f2d224694b2cfc Mon Sep 17 00:00:00 2001 From: Arthur Andrade Date: Thu, 26 May 2022 13:36:23 -0300 Subject: [PATCH] feat: Add OutOfStock component (#1314) Co-authored-by: Filipe W. Lima Co-authored-by: Eduardo Formiga --- packages/ui/src/index.ts | 10 ++ .../organisms/OutOfStock/OutOfStock.test.tsx | 122 ++++++++++++++++++ .../src/organisms/OutOfStock/OutOfStock.tsx | 39 ++++++ .../OutOfStock/OutOfStockMessage.tsx | 22 ++++ .../organisms/OutOfStock/OutOfStockTitle.tsx | 22 ++++ .../ui/src/organisms/OutOfStock/index.tsx | 6 + .../OutOfStock/stories/OutOfStock.mdx | 37 ++++++ .../OutOfStock/stories/OutOfStock.stories.tsx | 73 +++++++++++ .../src/organisms/index.css | 1 + .../src/organisms/out-of-stock.css | 27 ++++ 10 files changed, 359 insertions(+) create mode 100644 packages/ui/src/organisms/OutOfStock/OutOfStock.test.tsx create mode 100644 packages/ui/src/organisms/OutOfStock/OutOfStock.tsx create mode 100644 packages/ui/src/organisms/OutOfStock/OutOfStockMessage.tsx create mode 100644 packages/ui/src/organisms/OutOfStock/OutOfStockTitle.tsx create mode 100644 packages/ui/src/organisms/OutOfStock/index.tsx create mode 100644 packages/ui/src/organisms/OutOfStock/stories/OutOfStock.mdx create mode 100644 packages/ui/src/organisms/OutOfStock/stories/OutOfStock.stories.tsx create mode 100644 themes/theme-b2c-tailwind/src/organisms/out-of-stock.css diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 7d1c99d55a..2df99693ff 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -176,6 +176,16 @@ export type { } from './molecules/Dropdown' // Organisms +export { + default as OutOfStock, + OutOfStockTitle, + OutOfStockMessage, +} from './organisms/OutOfStock' +export type { + OutOfStockProps, + OutOfStockMessageProps, + OutOfStockTitleProps, +} from './organisms/OutOfStock' // Hooks export { default as useSlider } from './hooks/useSlider' diff --git a/packages/ui/src/organisms/OutOfStock/OutOfStock.test.tsx b/packages/ui/src/organisms/OutOfStock/OutOfStock.test.tsx new file mode 100644 index 0000000000..49d10955a0 --- /dev/null +++ b/packages/ui/src/organisms/OutOfStock/OutOfStock.test.tsx @@ -0,0 +1,122 @@ +import { render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { axe } from 'jest-axe' +import React from 'react' + +import { OutOfStockMessage, OutOfStockTitle } from '.' +import Button from '../../atoms/Button' +import Input from '../../atoms/Input' +import Label from '../../atoms/Label' +import OutOfStock from './OutOfStock' + +const SimpleOutOfStock = () => ( + + + Text icon + + Notify me when available + + + +) + +describe('OutOfStock', () => { + it('`Out Of Stock` components should have corrects attributes', () => { + render() + + const outOfStock = screen.getByTestId('store-out-of-stock') + const outOfStockTitle = screen.getByTestId('store-out-of-stock-title') + const outOfStockMessage = screen.getByTestId('store-out-of-stock-message') + const outOfStockForm = screen.getByTestId('store-out-of-stock-form') + + expect(outOfStock).toHaveAttribute('data-store-out-of-stock') + expect(outOfStockForm).toHaveAttribute('data-out-of-stock-form') + expect(outOfStockTitle).toHaveAttribute('data-out-of-stock-title') + expect(outOfStockMessage).toHaveAttribute('data-out-of-stock-message') + }) + + it('Should emit event', () => { + const onSubmitMock = jest.fn((e) => e.preventDefault()) + + render( + + Out of Stock + + + + ) + + const outOfStockEventButton = screen.getByTestId('store-button') + + userEvent.click(outOfStockEventButton) + + expect(onSubmitMock).toHaveBeenCalledTimes(1) + }) + + it('Should not render message', () => { + render( + + Out of Stock + + + + ) + + const message = screen.queryByTestId('store-out-of-stock-message') + + expect(message).not.toBeInTheDocument() + }) +}) + +describe('Accessibility', () => { + it('should not have violations or incompletes', async () => { + const { container } = render() + + expect(await axe(container)).toHaveNoViolations() + expect(await axe(container)).toHaveNoIncompletes() + }) + + it('Out of Stock component should be a `section`', () => { + render() + const outOfStock = screen.getByTestId('store-out-of-stock') + + expect(outOfStock.tagName).toEqual('SECTION') + }) + + it('Out of Stock `title` component should be a `heading 2` as default', () => { + render() + const outOfStockTitle = screen.getByTestId('store-out-of-stock-title') + + expect(outOfStockTitle.tagName).toEqual('H2') + }) + + it('Out of Stock `message` should be a `paragraph` as default', () => { + render() + const outOfStockMessage = screen.getByTestId('store-out-of-stock-message') + + expect(outOfStockMessage.tagName).toEqual('P') + }) + + it('Out of Stock should render `title` as heading 1 and `message` as span', () => { + render( + + Head Out Os Stock + Head Out Os Stock + + + + ) + + const outOfStockMessage = screen.getByTestId('store-out-of-stock-message') + const outOfStockTitle = screen.getByTestId('store-out-of-stock-title') + + expect(outOfStockTitle.tagName).toEqual('H1') + expect(outOfStockMessage.tagName).toEqual('SPAN') + }) +}) diff --git a/packages/ui/src/organisms/OutOfStock/OutOfStock.tsx b/packages/ui/src/organisms/OutOfStock/OutOfStock.tsx new file mode 100644 index 0000000000..ad030f2d06 --- /dev/null +++ b/packages/ui/src/organisms/OutOfStock/OutOfStock.tsx @@ -0,0 +1,39 @@ +import React from 'react' +import type { ReactNode, FormHTMLAttributes } from 'react' + +import Form from '../../molecules/Form' + +export type OutOfStockBaseProps = { + /** + * ID to find this component in testing tools (e.g.: cypress, + * testing-library, and jest). + */ + testId?: string + /** + * Children for Out of Stock components. + */ + children: string | ReactNode +} + +export type OutOfStockProps = OutOfStockBaseProps & { + /** + * Event emitted when form is submitted. + */ + onSubmit?: (event: React.FormEvent) => void +} & FormHTMLAttributes + +const OutOfStock = ({ + testId = 'store-out-of-stock', + children, + ...otherProps +}: OutOfStockProps) => { + return ( +
+
+ {children} +
+
+ ) +} + +export default OutOfStock diff --git a/packages/ui/src/organisms/OutOfStock/OutOfStockMessage.tsx b/packages/ui/src/organisms/OutOfStock/OutOfStockMessage.tsx new file mode 100644 index 0000000000..f57487ddce --- /dev/null +++ b/packages/ui/src/organisms/OutOfStock/OutOfStockMessage.tsx @@ -0,0 +1,22 @@ +import React from 'react' + +import type { OutOfStockBaseProps } from './OutOfStock' + +export type OutOfStockMessageProps = { + /** + * Attribute used for polymorphic component. + */ + as?: 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'div' | 'span' +} & OutOfStockBaseProps + +export const OutOfStockMessage = ({ + as: MessageComponent = 'p', + testId = 'store-out-of-stock-message', + children, +}: OutOfStockMessageProps) => { + return ( + + {children} + + ) +} diff --git a/packages/ui/src/organisms/OutOfStock/OutOfStockTitle.tsx b/packages/ui/src/organisms/OutOfStock/OutOfStockTitle.tsx new file mode 100644 index 0000000000..213dafac2a --- /dev/null +++ b/packages/ui/src/organisms/OutOfStock/OutOfStockTitle.tsx @@ -0,0 +1,22 @@ +import React from 'react' + +import type { OutOfStockBaseProps } from './OutOfStock' + +export type OutOfStockTitleProps = { + /** + * Attribute used for polymorphic component. + */ + as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' +} & OutOfStockBaseProps + +export const OutOfStockTitle = ({ + as: TitleComponent = 'h2', + testId = 'store-out-of-stock-title', + children, +}: OutOfStockTitleProps) => { + return ( + + {children} + + ) +} diff --git a/packages/ui/src/organisms/OutOfStock/index.tsx b/packages/ui/src/organisms/OutOfStock/index.tsx new file mode 100644 index 0000000000..7ff13c78c5 --- /dev/null +++ b/packages/ui/src/organisms/OutOfStock/index.tsx @@ -0,0 +1,6 @@ +export { default } from './OutOfStock' +export { OutOfStockMessage } from './OutOfStockMessage' +export { OutOfStockTitle } from './OutOfStockTitle' +export type { OutOfStockProps } from './OutOfStock' +export type { OutOfStockMessageProps } from './OutOfStockMessage' +export type { OutOfStockTitleProps } from './OutOfStockTitle' diff --git a/packages/ui/src/organisms/OutOfStock/stories/OutOfStock.mdx b/packages/ui/src/organisms/OutOfStock/stories/OutOfStock.mdx new file mode 100644 index 0000000000..624a47c112 --- /dev/null +++ b/packages/ui/src/organisms/OutOfStock/stories/OutOfStock.mdx @@ -0,0 +1,37 @@ +import { Canvas, Props, Story, ArgsTable } from '@storybook/addon-docs' + +import OutOfStock from '../OutOfStock' + +# OutOfStock + + + + + +The `OutOfStock` uses the [Compound Component](https://kentcdodds.com/blog/compound-components-with-react-hooks) pattern, its components are: + +- `OutOfStock`: the form wrapper of the out of stock component with title and message; +- `OutOfStockTitle`: the main title component for out of stock component; +- `OutOfStockMessage`: the message component for out of stock component; + +## Props + +### `OutOfStock` + + + +## CSS Selectors + +```css +[data-store-out-of-stock] { +} + +[data-out-of-stock-form] { +} + +[data-out-of-stock-title] { +} + +[data-out-of-stock-message] { +} +``` diff --git a/packages/ui/src/organisms/OutOfStock/stories/OutOfStock.stories.tsx b/packages/ui/src/organisms/OutOfStock/stories/OutOfStock.stories.tsx new file mode 100644 index 0000000000..4aeb55a3a6 --- /dev/null +++ b/packages/ui/src/organisms/OutOfStock/stories/OutOfStock.stories.tsx @@ -0,0 +1,73 @@ +import type { Story, Meta } from '@storybook/react' +import React, { useState } from 'react' + +import type { + OutOfStockMessageProps, + OutOfStockTitleProps, + OutOfStockProps, +} from '..' +import Component, { OutOfStockTitle, OutOfStockMessage } from '..' +import Button from '../../../atoms/Button' +import Input from '../../../atoms/Input' +import mdx from './OutOfStock.mdx' + +type OutOfStockTemplateProps = { + title: string + message: string + titleAs: OutOfStockTitleProps['as'] + messageAs: OutOfStockMessageProps['as'] +} & OutOfStockProps + +const OutOfStockTemplate: Story = ({ + title, + message, + titleAs, + messageAs, + ...props +}) => { + const [value, setValue] = useState('') + + const handlerSubmitForm = (e: React.FormEvent) => { + e.preventDefault() + + // eslint-disable-next-line no-alert + alert(value) + } + + return ( + + {title} + {message} + setValue(e.target.value)} /> + + + ) +} + +export const OutOfStock = OutOfStockTemplate.bind({}) +OutOfStock.storyName = 'OutOfStock' + +export default { + title: 'Organisms/OutOfStock', + parameters: { + docs: { + page: mdx, + }, + }, + args: { + title: 'Notify me', + message: 'Notify me when available', + }, + argTypes: { + titleAs: { + defaultValue: 'h2', + options: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p'], + control: { type: 'select' }, + }, + messageAs: { + defaultValue: 'p', + options: ['h2', 'h3', 'h4', 'h5', 'h6', 'p', 'div', 'span'], + control: { type: 'select' }, + }, + }, +} as Meta diff --git a/themes/theme-b2c-tailwind/src/organisms/index.css b/themes/theme-b2c-tailwind/src/organisms/index.css index e69de29bb2..013d69524b 100644 --- a/themes/theme-b2c-tailwind/src/organisms/index.css +++ b/themes/theme-b2c-tailwind/src/organisms/index.css @@ -0,0 +1 @@ +@import './out-of-stock.css'; diff --git a/themes/theme-b2c-tailwind/src/organisms/out-of-stock.css b/themes/theme-b2c-tailwind/src/organisms/out-of-stock.css new file mode 100644 index 0000000000..e09f3afa9a --- /dev/null +++ b/themes/theme-b2c-tailwind/src/organisms/out-of-stock.css @@ -0,0 +1,27 @@ +[data-store-out-of-stock] [data-out-of-stock-form] { + display: flex; + align-items: center; + flex-direction: column; +} + +[data-store-out-of-stock] [data-out-of-stock-title] { + margin-bottom: .25rem; + font-size: inherit; + font-weight: inherit; +} + +[data-store-out-of-stock] [data-out-of-stock-message] { + align-items: center; + margin-bottom: 1rem; +} + +[data-out-of-stock-form] [data-store-button] { + width: 100%; + margin-top: 1rem; +} + +[data-out-of-stock-form] [data-store-input] { + width: 100%; + margin-top: 0.25rem; + max-width: initial; +}