Commit efb5f708 by Syed Abdul Rahman

implementing components in story book

parent a22c9d37
......@@ -22,3 +22,6 @@ dist-ssr
*.njsproj
*.sln
*.sw?
*storybook.log
storybook-static
/** @type { import('@storybook/react-vite').StorybookConfig } */
const config = {
"stories": [
"../src/**/*.mdx",
"../src/components/**/*.stories.@(js|jsx|ts|tsx)",
],
"addons": [
"@storybook/addon-onboarding",
"@chromatic-com/storybook",
"@storybook/addon-docs",
"@storybook/addon-a11y",
"@storybook/addon-vitest"
],
"framework": {
"name": "@storybook/react-vite",
"options": {}
}
};
export default config;
\ No newline at end of file
/** @type { import('@storybook/react-vite').Preview } */
import '../src/assets/fonts.css'
const preview = {
parameters: {
backgrounds: {
options: {
// Define your custom background colors
light: { name: 'Light', value: '#F7F9F2' },
dark: { name: 'Dark', value: '#333' },
maroon: { name: 'Maroon', value: '#400' },
myCustomBlue: { name: 'My Custom Blue', value: '#3444c5' }, // Example
},
},
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
a11y: {
// 'todo' - show a11y violations in the test UI only
// 'error' - fail CI on a11y violations
// 'off' - skip a11y checks entirely
test: "todo"
}
},
};
export default preview;
\ No newline at end of file
import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview";
import { setProjectAnnotations } from '@storybook/react-vite';
import * as projectAnnotations from './preview';
// This is an important step to apply the right configuration when testing your stories.
// More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations
setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]);
// .storybook/preview.js
export const parameters = {
backgrounds: {
default: 'custom',
values: [
{
name: 'custom',
value: 'red', // 👈 Your desired canvas background color
},
],
},
};
// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format
import storybook from "eslint-plugin-storybook";
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
export default [
{ ignores: ['dist'] },
{
export default [{ ignores: ['dist'] }, {
files: ['**/*.{js,jsx}'],
languageOptions: {
ecmaVersion: 2020,
......@@ -29,5 +30,4 @@ export default [
{ allowConstantExport: true },
],
},
},
]
}, ...storybook.configs["flat/recommended"]];
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -7,21 +7,36 @@
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
"preview": "vite preview",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"dependencies": {
"react": "^19.1.0",
"react-dom": "^19.1.0"
},
"devDependencies": {
"@chromatic-com/storybook": "^4.0.0",
"@eslint/js": "^9.25.0",
"@storybook/addon-a11y": "^9.0.4",
"@storybook/addon-docs": "^9.0.4",
"@storybook/addon-onboarding": "^9.0.4",
"@storybook/addon-vitest": "^9.0.4",
"@storybook/react-vite": "^9.0.4",
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2",
"@vitejs/plugin-react": "^4.4.1",
"@vitest/browser": "^3.2.0",
"@vitest/coverage-v8": "^3.2.0",
"eslint": "^9.25.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19",
"eslint-plugin-storybook": "^9.0.4",
"globals": "^16.0.0",
"vite": "^6.3.5"
"playwright": "^1.52.0",
"prop-types": "^15.8.1",
"storybook": "^9.0.4",
"vite": "^6.3.5",
"vitest": "^3.2.0"
}
}
import './App.css'
import Button from './components/Base/Button/Index'
function App() {
return (
<>
<h3> Booking APP</h3>
<Button>Confirm</Button>
</>
)
}
......
@font-face {
font-family: 'Poppins-Medium';
src: url('../assets/fonts/Poppins-Medium.ttf');
}
@font-face {
font-family: 'Poppins-Bold';
src: url('../assets/fonts/Poppins-Bold.ttf');
}
\ No newline at end of file
import Button from './Index';
export default {
title: 'Component/Base/Button',
tags: ['autodocs'],
component: Button
}
export const Primary = {
args: {
primary: true,
label: 'Button',
},
render: () => {
return <Button>Confirm</Button>
}
};
\ No newline at end of file
import PropTypes from 'prop-types';
import styles from './styles.module.css';
const Button = (props) => {
const {
title,
onConfirm
} = props;
const Button = ({ loading, size, disabled, children, ...props }) => {
return (
<button className={styles.button} onClick={onConfirm}>{title}</button>
<button className={styles.button} {...props}>{children}</button>
)
}
......@@ -18,7 +12,9 @@ export default Button;
Button.PropTypes = {
title: PropTypes.string.isRequired,
onConfirm: PropTypes.func.isRequired,
children: PropTypes.node.isRequired,
disabled: PropTypes.bool,
onClick: PropTypes.func,
size: PropTypes.string,
loading: PropTypes.bool
}
\ No newline at end of file
......@@ -3,4 +3,9 @@
outline: none;
background-color: white;
color: black;
border-radius: 5px;
padding: 0.5rem 1.5rem;
font-family: 'Poppins-Bold';
cursor: pointer;
font-size: 18px;
}
\ No newline at end of file
import PropTypes from 'prop-types';
const Input = (props) => {
const Input = ({ ...inputPprops }) => {
const { onValueChange } = props;
return (
<input onChange={onValueChange} />
<input {...inputPprops} />
)
}
......@@ -13,5 +12,8 @@ export default Input;
Input.PropTypes = {
onValueChange: PropTypes.func.isRequired
onChange: PropTypes.func.isRequired,
type: PropTypes.string,
value: PropTypes.string.isRequired,
placeholder: PropTypes.string
}
\ No newline at end of file
import styles from './styles.module.css';
import PropTypes from 'prop-types';
const Legend = (props) => {
const { title, type } = props;
const Legend = ({ children, type="selected" }) => {
return (
<div>Legend</div>
<div className={styles['legend-wrapper']}>
<div className={styles[type]}></div>
<div className={styles.title}>{children}</div>
</div>
)
}
Legend.PropTypes = {
children: PropTypes.node.isRequired,
type: PropTypes.oneOf(["selected", "reserved", "available"])
}
export default Legend;
import Legend from "./Index"
export default {
title: "Component/Base/Legend",
tags: ["autodocs"],
component: Legend
}
export const Selected = {
args: {
type: 'selected'
},
render: (args) => {
return <Legend type={args.type}>Selected</Legend>
}
};
export const Reserved = {
args: {
type: 'reserved'
},
render: (args) => {
return <Legend type={args.type}>Reserved</Legend>
}
};
export const Available = {
args: {
type: 'available'
},
render: (args) => {
return <Legend type={args.type}>available</Legend>
}
};
\ No newline at end of file
.selected {
width: 15px;
height: 15px;
border-radius: 50%;
background-color: aqua;
}
.reserved {
width: 15px;
height: 15px;
border-radius: 50%;
background-color: rgb(151, 155, 155);
}
.available {
width: 15px;
height: 15px;
border-radius: 50%;
border: 1px solid white;
}
.legend-wrapper {
display: flex;
gap: 10px;
align-items: center;
}
.title {
color: white;
font-family: 'Poppins-Medium';
}
\ No newline at end of file
import BookingWrapper from "./Index";
export default {
title: 'Component/Layout/BookingWrapper',
tags: ["autodocs"],
component: BookingWrapper
}
export const Default = {
}
\ No newline at end of file
import PropTypes from 'prop-types';
const BookingWrapper = (props) => {
const BookingWrapper = () => {
const aisleIndex = 4;
const seatData = Array.from({ length: 10 }, (_, index) => ({
id: index + 1,
status: 'available'
}));
const { seatData } = props;
const isLeftBlock = (col) => col < aisleIndex;
const isRightBlock = (col) => col > aisleIndex;
return (
<div>
<div className='theatre'>
{Array.from({ length: seatData.row }, (_, row_index) => (
{Array.from({ length: 5 }, (_, row_index) => (
<div className='seat-row'>
{Array.from({ length: seatData.colum }, (_, column_index) => {
{seatData.map((_, column_index) => {
const isFirstRow = row_index === 0;
const isLastRow = row_index === seatData.row - 1;
......@@ -19,12 +25,12 @@ const BookingWrapper = (props) => {
if (
isRightBlock(column_index) &&
column_index === seatData.colum - 1 &&
column_index === seatData.id - 1 &&
(isFirstRow || isLastRow)
) {
return <div key={`gap-right-${row_index}-${row_index}`} className="seat-gap" />;
}
if (column_index == seatData.aisleIndex) {
if (column_index == aisleIndex) {
return (
<div className='aisle'></div>
)
......@@ -43,10 +49,3 @@ const BookingWrapper = (props) => {
export default BookingWrapper;
BookingWrapper.PropTypes = {
seatData: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
status: PropTypes.oneOf(['available', 'reserved', 'selected']).isRequired,
})).isRequired
}
\ No newline at end of file
import styles from './styles.module.css'
import PropTypes from 'prop-types';
const Seat = (props) => {
const {onSeatClick, status} = props
return (
<div onClick={onSeatClick} className={` ${styles[status]} ${styles.seat} `}>
const Seat = ({ status, ...rest }) => {
</div>
return (
<div {...rest} className={` ${styles[status]} ${styles.seat} `}></div>
)
}
Seat.PropTypes = {
onSeatClick: PropTypes.func.isRequired,
onClick: PropTypes.func.isRequired,
status: PropTypes.oneOf(['available', 'reserved', 'selected'])
}
......
import Seat from './Seat';
export default {
tags: ["autodocs"],
title: "Component/Layout/BookingWrapper/Seat",
component: Seat
}
export const Available = {
args: {
},
render: () => {
return <Seat status={"available"}/>
}
}
export const Selected = {
args: {
},
render: () => {
return <Seat status={"selected"}/>
}
}
export const Reserved = {
args: {
},
render: () => {
return <Seat status={"reserved"}/>
}
}
\ No newline at end of file
......@@ -5,3 +5,11 @@
border-radius: 5px;
cursor: pointer;
}
.reserved{
background-color: gray;
}
.selected{
background-color: aqua;
}
\ No newline at end of file
import Header from './Index'
export default {
title: "Component/Layout/Header",
tags: ["autodocs"],
component: Header
}
export const Default = {
args: {
},
render: () => {
return <Header>Choose Seats</Header>
}
}
\ No newline at end of file
import PropTypes from 'prop-types';
import styles from './styles.module.css';
const Header = (props) => {
const Header = ({ children }) => {
const { title } = props;
return (
<div>{title}</div>
<section className={styles['header-wrapper']}>
<div className={styles.title}>
{children}
</div>
<div style={{width: '60%'}}>
<svg viewBox="0 0 480 260" xmlns="http://www.w3.org/2000/svg">
<path d="M 30 70 Q 240 20 450 70" stroke="white" stroke-width="5" fill="none" />
<defs>
<linearGradient id="glowGradient" x1="0" y1="70" x2="0" y2="180" gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="#addfff" stop-opacity="0.15" />
<stop offset="100%" stop-color="#addfff" stop-opacity="0" />
</linearGradient>
<clipPath id="glowClip">
<path d="
M 30 70
Q 240 20 450 70
L 470 180
L 10 180
Z" />
</clipPath>
</defs>
<rect x="0" y="0" width="480" height="260"
fill="url(#glowGradient)"
clip-path="url(#glowClip)" />
</svg>
</div>
</section>
)
}
Header.PropTypes = {
title: PropTypes.string.isRequired
children: PropTypes.node.isRequired
}
export default Header
\ No newline at end of file
.title{
font-family: 'Poppins-Bold';
color: white;
font-size: 24px;
}
.header-wrapper{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 1rem;
}
\ No newline at end of file
import styles from './styles.module.css';
import PropTypes from 'prop-types';
import Legend from '../../Base/Legend/Index'
const LegendWrapper = (props) => {
const {
legendData
} = props;
const legendData = [
{
id: 1,
label: "Selected",
status: 'selected'
},
{
id: 2,
label: "Reserved",
status: "reserved"
},
{
id: 3,
label: "Available",
status: "available"
}
]
return (
<div>LegendWrapper</div>
<div className={styles.LegendWrapper}>
{legendData.map((ele) => (
<Legend type={ele.status}>{ele.label}</Legend>
))}
</div>
)
}
export default LegendWrapper;
LegendWrapper.PropTypes = {
legendData: PropTypes.arrayOf(
PropTypes.shape({
title: PropTypes.string,
status: PropTypes.oneOf(['available', 'reserved', 'selected']).isRequired,
})
)
}
\ No newline at end of file
import LegendWrapper from "./Index";
export default {
title: "Component/TopLevel/LegendWrapper",
tags: ["autodocs"],
component: LegendWrapper
}
export const Default = {
}
\ No newline at end of file
.LegendWrapper {
display: flex;
gap: 1rem;
}
\ No newline at end of file
@import '../src//assets//fonts.css';
*{
padding: 0;
margin: 0;
......
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { defineWorkspace } from 'vitest/config';
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
const dirname =
typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
// More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon
export default defineWorkspace([
'vite.config.js',
{
extends: 'vite.config.js',
plugins: [
// The plugin will run tests for the stories defined in your Storybook config
// See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest
storybookTest({ configDir: path.join(dirname, '.storybook') }),
],
test: {
name: 'storybook',
browser: {
enabled: true,
headless: true,
provider: 'playwright',
instances: [{ browser: 'chromium' }]
},
setupFiles: ['.storybook/vitest.setup.js'],
},
},
]);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment