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

Accordion Variants #787

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/components/ui/Accordion/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@ export interface AccordionItemType {

export interface AccordionProps {
items: AccordionItemType[];
variant?: string;
color?: string;
}

const Accordion = ({ items }: AccordionProps) => {
const Accordion = ({ items, variant = '', color = '' }: AccordionProps) => {
return (
<AccordionRoot>
<AccordionRoot variant={variant} color={color}>
{items.map((item, index) => (
<AccordionItem value={index} key={index} >
<AccordionHeader>
<AccordionTrigger >
<AccordionTrigger>
{item.title}
</AccordionTrigger>
</AccordionHeader>
Expand Down
12 changes: 9 additions & 3 deletions src/components/ui/Accordion/fragments/AccordionHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import React from 'react';
import React, { useContext } from 'react';
import { clsx } from 'clsx';

import { AccordionContext } from '../contexts/AccordionContext';
import { AccordionItemContext } from '../contexts/AccordionItemContext';
export type AccordionHeaderProps = {
children: React.ReactNode;
className?: string;
}

const AccordionHeader: React.FC<AccordionHeaderProps> = ({ children, className = '' }) => {
const { setActiveItem, rootClass, focusNextItem, focusPrevItem, activeItem } = useContext(AccordionContext);
const { itemValue, handleBlurEvent, handleClickEvent, handleFocusEvent } = useContext(AccordionItemContext);

const expanded = activeItem === itemValue;

return (
<div className={clsx(className)}>
<div className={clsx(`${rootClass}-header`, className)} data-expanded={expanded}>
{children}
</div>
);
Expand Down
20 changes: 16 additions & 4 deletions src/components/ui/Accordion/fragments/AccordionRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,33 @@ const COMPONENT_NAME = 'Accordion';
export type AccordionRootProps = {
children: React.ReactNode;
customRootClass?: string;
variant?: string | ''
color?: string | ''
loop?: boolean
}

const AccordionRoot = ({ children, customRootClass }: AccordionRootProps) => {
const AccordionRoot = ({ children, customRootClass, variant = '', color = '', loop = true }: AccordionRootProps) => {
const accordionRef = useRef<HTMLDivElement | null>(null);
const rootClass = customClassSwitcher(customRootClass, COMPONENT_NAME);

const [activeItem, setActiveItem] = useState<number | null>(null);
const [focusItem, setFocusItem] = useState<Element | null>(null); // stores the id of the item that should be focused

const dataAttributes: Record<string, string> = {};
if (variant) {
dataAttributes['data-variant'] = variant;
}

if (color) {
dataAttributes['data-accent-color'] = color;
}

useEffect(() => {}, []);

const focusNextItem = () => {
if (!accordionRef.current) return;
const batches = getAllBatchElements(accordionRef?.current);
const nextItem = getNextBatchItem(batches);
const nextItem = getNextBatchItem(batches, loop);
setFocusItem(nextItem);
if (nextItem) {
const button = nextItem.querySelector('button');
Expand All @@ -35,7 +47,7 @@ const AccordionRoot = ({ children, customRootClass }: AccordionRootProps) => {
const focusPrevItem = () => {
if (!accordionRef.current) return;
const batches = getAllBatchElements(accordionRef?.current);
const prevItem = getPrevBatchItem(batches);
const prevItem = getPrevBatchItem(batches, loop);
setFocusItem(prevItem);
if (prevItem) {
const button = prevItem.querySelector('button');
Expand All @@ -57,7 +69,7 @@ const AccordionRoot = ({ children, customRootClass }: AccordionRootProps) => {
accordionRef

}}>
<div className={clsx(`${rootClass}-root`)} ref={accordionRef}>
<div className={clsx(`${rootClass}-root`)} ref={accordionRef} {...dataAttributes} >
{children}
</div>
</AccordionContext.Provider>
Expand Down
1 change: 0 additions & 1 deletion src/components/ui/Accordion/fragments/AccordionTrigger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ const AccordionTrigger: React.FC<AccordionTriggerProps> = ({ children, index, cl
};

return (

<button
type="button"
className={clsx(`${rootClass}-trigger`, className)}
Expand Down
103 changes: 60 additions & 43 deletions src/components/ui/Accordion/stories/Accordion.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,51 @@
import Accordion from '../Accordion';
import Accordion, { AccordionProps } from '../Accordion';
import SandboxEditor from '~/components/tools/SandboxEditor/SandboxEditor';
import type { Meta, StoryObj } from '@storybook/react';
import React from 'react';

const items = [
{
title: 'The Matrix (1999)',
content: <div>
<ul>
<li> Summary: A hacker discovers the true nature of reality and his role in the war against its controllers.</li>
<li>Key Characters: Neo, Morpheus, Trinity, Agent Smith</li>
<li>Memorable Quote: &quot;There is no spoon.&quot;</li>
</ul>
</div>
},
{
title: 'The Dark Knight (2008)',
content: <div>
<ul>
<li> Summary: Batman faces his greatest challenge yet as the Joker wreaks havoc on Gotham City.</li>
<li>Key Characters: Batman, Joker, Harvey Dent, Alfred</li>
<li>Memorable Quote: &quot;Why so serious?&quot;</li>
</ul>
</div>
},
{
title: 'Inception (2010)',
content: <div>
<ul>
<li> Summary: A thief who enters people&apos;s dreams to steal their secrets must plant an idea in someone&apos;s mind.</li>
<li>Key Characters: Cobb, Ariadne, Mal, Saito</li>
<li>Memorable Quote: &quot;You mustn&apos;t be afraid to dream a little bigger, darling.&quot;</li>
</ul>
</div>
},
{
title: 'The Shawshank Redemption (1994)',
content: <div>
<ul>
<li> Summary: A banker is wrongly convicted</li>
<li>Key Characters: Andy Dufresne, Red, Warden Norton, Tommy</li>
<li>Memorable Quote: &quot;Get busy living or get busy dying.&quot;</li>
</ul>
</div>
}
];

// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
const meta: Meta<typeof Accordion> = {
title: 'Components/Accordion',
Expand All @@ -24,47 +67,21 @@ type Story = StoryObj<typeof Accordion>;
// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
export const All: Story = {
args: {
items: [
{
title: 'The Matrix (1999)',
content: <div>
<ul>
<li> Summary: A hacker discovers the true nature of reality and his role in the war against its controllers.</li>
<li>Key Characters: Neo, Morpheus, Trinity, Agent Smith</li>
<li>Memorable Quote: &quot;There is no spoon.&quot;</li>
</ul>
</div>
},
{
title: 'The Dark Knight (2008)',
content: <div>
<ul>
<li> Summary: Batman faces his greatest challenge yet as the Joker wreaks havoc on Gotham City.</li>
<li>Key Characters: Batman, Joker, Harvey Dent, Alfred</li>
<li>Memorable Quote: &quot;Why so serious?&quot;</li>
</ul>
</div>
},
{
title: 'Inception (2010)',
content: <div>
<ul>
<li> Summary: A thief who enters people&apos;s dreams to steal their secrets must plant an idea in someone&apos;s mind.</li>
<li>Key Characters: Cobb, Ariadne, Mal, Saito</li>
<li>Memorable Quote: &quot;You mustn&apos;t be afraid to dream a little bigger, darling.&quot;</li>
</ul>
</div>
},
{
title: 'The Shawshank Redemption (1994)',
content: <div>
<ul>
<li> Summary: A banker is wrongly convicted</li>
<li>Key Characters: Andy Dufresne, Red, Warden Norton, Tommy</li>
<li>Memorable Quote: &quot;Get busy living or get busy dying.&quot;</li>
</ul>
</div>
}
]
items,
variant: 'solid'
}
};

export const Outline: Story = {
args: {
items,
variant: 'outline'
}
};

export const Color: Story = {
args: {
items,
color: 'blue'
}
};
7 changes: 6 additions & 1 deletion src/components/ui/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ const COMPONENT_NAME = 'Button';
export type ButtonProps = {
customRootClass?: string;
variant?: string;
color?: string;
size?:string;
} & DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> & PropsWithChildren

const Button = ({ children, type = 'button', customRootClass = '', className = '', variant = '', size = '', ...props }: ButtonProps) => {
const Button = ({ children, type = 'button', customRootClass = '', className = '', variant = '', size = '', color = '', ...props }: ButtonProps) => {
const rootClass = customClassSwitcher(customRootClass, COMPONENT_NAME);
// apply data attribute for accent color
// apply attribute only if color is present
Expand All @@ -27,6 +28,10 @@ const Button = ({ children, type = 'button', customRootClass = '', className = '
data_attributes['data-button-size'] = size;
}

if (color) {
data_attributes['data-accent-color'] = color;
}

return (
<ButtonPrimitive
type={type}
Expand Down
21 changes: 20 additions & 1 deletion src/components/ui/Button/stories/Button.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default {
};

// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
export const All = {
export const AllVariants = {
args: {
className: ''
}
Expand Down Expand Up @@ -65,3 +65,22 @@ export const Size = (args) => {
</div>
</SandboxEditor>;
};

export const Color = (args) => {
return <SandboxEditor>
<div className='flex items-center space-x-[40px]'>
<div>
<p>Colored Button</p>
<Button color='red' >
<div>{BUTTON_TEXT} </div> <ArrowIcon />
</Button>
</div>
<div>
<p>Theme Button</p>
<Button>
<div>{BUTTON_TEXT} </div> <ArrowIcon />
</Button>
</div>
</div>
</SandboxEditor>;
};
4 changes: 2 additions & 2 deletions src/components/ui/Code/tests/Code.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ describe('Code Component', () => {

const codeElement = container.querySelector('code');
expect(codeElement).toBeInTheDocument();
expect(codeElement).toHaveTextContent("console.log('Hello world!');");
expect(codeElement).toHaveTextContent('console.log(\'Hello world!\');');
});
})
});
46 changes: 23 additions & 23 deletions src/components/ui/Em/tests/Em.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,32 @@ import Em from '../Em';

describe('Em', () => {
test('renders Em component', () => {
render(<>Welcome to <Em>RadUI</Em></>);
expect(screen.getByText('RadUI')).toBeInTheDocument();
});
render(<>Welcome to <Em>RadUI</Em></>);
expect(screen.getByText('RadUI')).toBeInTheDocument();
});

test('renders Em tag with valid text', () => {
render(<>Welcome to <Em>RadUI</Em></>);
expect(screen.getByText('RadUI').tagName.toLowerCase()).toBe('em');
});
render(<>Welcome to <Em>RadUI</Em></>);
expect(screen.getByText('RadUI').tagName.toLowerCase()).toBe('em');
});

test('renders custom classes correctly', () => {
render(<>Welcome to <Em className="custom-class">RadUI</Em></>);
expect(screen.getByText('RadUI')).toHaveClass('custom-class');
});
render(<>Welcome to <Em className="custom-class">RadUI</Em></>);
expect(screen.getByText('RadUI')).toHaveClass('custom-class');
});

test('renders custom styles correctly', () => {
render(<>Welcome to <Em style={{ color: 'red' }}>RadUI</Em></>);
expect(screen.getByText('RadUI')).toHaveStyle('color: red');
});
render(<>Welcome to <Em style={{ color: 'red' }}>RadUI</Em></>);
expect(screen.getByText('RadUI')).toHaveStyle('color: red');
});

test('renders custom id correctly', () => {
render(<>Welcome to <Em id="em-id">RadUI</Em></>);
expect(screen.getByText('RadUI')).toHaveAttribute('id', 'em-id');
});
render(<>Welcome to <Em id="em-id">RadUI</Em></>);
expect(screen.getByText('RadUI')).toHaveAttribute('id', 'em-id');
});

test('renders custom data attribute correctly', () => {
render(<>Welcome to <Em data-testid="em-data">RadUI</Em></>);
expect(screen.getByText('RadUI')).toHaveAttribute('data-testid', 'em-data');
});
});
render(<>Welcome to <Em data-testid="em-data">RadUI</Em></>);
expect(screen.getByText('RadUI')).toHaveAttribute('data-testid', 'em-data');
});
});
4 changes: 2 additions & 2 deletions src/components/ui/Table/tests/Table.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ describe('Table Component', () => {
{ id: 1, fullName: 'John Smith', age: 23, isIntern: 'No' },
{ id: 2, fullName: 'Anna Donie', age: 35, isIntern: 'Yes' },
{ id: 3, fullName: 'Hannah Brown', age: 20, isIntern: 'Yes' },
{ id: 4, fullName: 'Johnathan White Jr', age: 36, isIntern: 'No' },
{ id: 4, fullName: 'Johnathan White Jr', age: 36, isIntern: 'No' }
];

const employeeKey = [
{ key: 'id', name: 'Employee Id' },
{ key: 'fullName', name: 'Full Name' },
{ key: 'age', name: 'Age' },
{ key: 'isIntern', name: 'In Internship' },
{ key: 'isIntern', name: 'In Internship' }
];

let result: RenderResult;
Expand Down
Loading
Loading