Skip to content
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
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ jobs:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
exitZeroOnChanges: true
allowConsoleErrors: true
onlyChanged: true

- name: Save release version to outputs
id: save_release_version
Expand Down
368 changes: 368 additions & 0 deletions __tests__/Accordion.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,368 @@
import { fireEvent, render } from '@testing-library/react';
import {
ReqoreAccordion,
ReqoreContent,
ReqoreLayoutContent,
ReqoreUIProvider,
} from '../src';

const basicItems = [
{ title: 'Item 1', content: 'Content 1' },
{ title: 'Item 2', content: 'Content 2' },
{ title: 'Item 3', content: 'Content 3' },
];

test('Renders <Accordion /> with items', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion items={basicItems} />
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

expect(document.querySelectorAll('.reqore-accordion').length).toBe(1);
expect(document.querySelectorAll('.reqore-accordion-item').length).toBe(3);
expect(document.querySelectorAll('.reqore-accordion-header').length).toBe(3);
});

test('Renders all item titles', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion items={basicItems} />
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

expect(document.querySelectorAll('.reqore-accordion-title').length).toBe(3);
expect(document.querySelectorAll('.reqore-accordion-title')[0].textContent).toBe('Item 1');
expect(document.querySelectorAll('.reqore-accordion-title')[1].textContent).toBe('Item 2');
expect(document.querySelectorAll('.reqore-accordion-title')[2].textContent).toBe('Item 3');
});

test('Toggles item on header click', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion items={basicItems} />
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

const headers = document.querySelectorAll('.reqore-accordion-header');

// Click to open first item
fireEvent.click(headers[0]);

// The header should have aria-expanded=true
expect(headers[0].getAttribute('aria-expanded')).toBe('true');

// Click again to close
fireEvent.click(headers[0]);
expect(headers[0].getAttribute('aria-expanded')).toBe('false');
});

test('Respects isOpen default state', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion
items={[
{ title: 'Open', content: 'Open content', isOpen: true },
{ title: 'Closed', content: 'Closed content' },
]}
/>
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

const headers = document.querySelectorAll('.reqore-accordion-header');
expect(headers[0].getAttribute('aria-expanded')).toBe('true');
expect(headers[1].getAttribute('aria-expanded')).toBe('false');
});

test('Allows multiple items open when allowMultiple=true', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion items={basicItems} allowMultiple />
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

const headers = document.querySelectorAll('.reqore-accordion-header');

// Open first
fireEvent.click(headers[0]);
expect(headers[0].getAttribute('aria-expanded')).toBe('true');

// Open second — first should stay open
fireEvent.click(headers[1]);
expect(headers[0].getAttribute('aria-expanded')).toBe('true');
expect(headers[1].getAttribute('aria-expanded')).toBe('true');
});

test('Only one item open when allowMultiple=false', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion items={basicItems} allowMultiple={false} />
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

const headers = document.querySelectorAll('.reqore-accordion-header');

// Open first
fireEvent.click(headers[0]);
expect(headers[0].getAttribute('aria-expanded')).toBe('true');

// Open second — first should close
fireEvent.click(headers[1]);
expect(headers[0].getAttribute('aria-expanded')).toBe('false');
expect(headers[1].getAttribute('aria-expanded')).toBe('true');
});

test('Renders with icons', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion
items={[
{ title: 'With icon', content: 'Content', icon: 'Settings3Line' },
{ title: 'No icon', content: 'Content' },
]}
/>
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

expect(document.querySelectorAll('.reqore-accordion-icon').length).toBe(1);
});

test('Renders with badges', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion
items={[
{ title: 'With badge', content: 'Content', badge: 5 },
{ title: 'No badge', content: 'Content' },
]}
/>
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

expect(document.querySelectorAll('.reqore-accordion-badge').length).toBe(1);
});

test('Disabled items cannot be toggled', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion
items={[
{ title: 'Disabled', content: 'Content', disabled: true },
{ title: 'Normal', content: 'Content' },
]}
/>
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

const headers = document.querySelectorAll('.reqore-accordion-header');

// Click disabled item
fireEvent.click(headers[0]);
expect(headers[0].getAttribute('aria-expanded')).toBe('false');

// Normal item should still work
fireEvent.click(headers[1]);
expect(headers[1].getAttribute('aria-expanded')).toBe('true');
});

test('Globally disabled accordion prevents all toggling', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion items={basicItems} disabled />
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

expect(document.querySelectorAll('.reqore-accordion').length).toBe(1);
});

test('Keyboard: Enter toggles item', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion items={basicItems} />
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

const header = document.querySelectorAll('.reqore-accordion-header')[0];
fireEvent.keyDown(header, { key: 'Enter' });
expect(header.getAttribute('aria-expanded')).toBe('true');
});

test('Keyboard: Space toggles item', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion items={basicItems} />
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

const header = document.querySelectorAll('.reqore-accordion-header')[0];
fireEvent.keyDown(header, { key: ' ' });
expect(header.getAttribute('aria-expanded')).toBe('true');
});

test('Calls onItemToggle callback', () => {
const handleToggle = jest.fn();

render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion items={basicItems} onItemToggle={handleToggle} />
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

fireEvent.click(document.querySelectorAll('.reqore-accordion-header')[1]);
expect(handleToggle).toHaveBeenCalledWith(1, true);

// Toggle again to close
fireEvent.click(document.querySelectorAll('.reqore-accordion-header')[1]);
expect(handleToggle).toHaveBeenCalledWith(1, false);
});

test('Renders with different sizes', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion items={basicItems} size='tiny' />
<ReqoreAccordion items={basicItems} size='huge' />
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

expect(document.querySelectorAll('.reqore-accordion').length).toBe(2);
});

test('Renders with intents', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion items={basicItems} intent='info' />
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

expect(document.querySelectorAll('.reqore-accordion').length).toBe(1);
});

test('Renders with item-level intents', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion
items={[
{ title: 'Info', content: 'Content', intent: 'info' },
{ title: 'Danger', content: 'Content', intent: 'danger' },
]}
/>
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

expect(document.querySelectorAll('.reqore-accordion-item').length).toBe(2);
});

test('Renders flat accordion', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion items={basicItems} flat />
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

expect(document.querySelectorAll('.reqore-accordion').length).toBe(1);
});

test('Renders fluid accordion', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion items={basicItems} fluid />
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

expect(document.querySelectorAll('.reqore-accordion').length).toBe(1);
});

test('Renders custom React content', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreContent>
<ReqoreAccordion
items={[
{
title: 'Custom',
content: <div className='custom-content'>Custom element</div>,
isOpen: true,
},
]}
/>
</ReqoreContent>
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

expect(document.querySelectorAll('.custom-content').length).toBe(1);
expect(document.querySelector('.custom-content')!.textContent).toBe('Custom element');
});
6 changes: 5 additions & 1 deletion chromatic.config.json
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
{"projectId":"Project:61cd8c0230613d003a66b81d","projectToken":"2610971c3349"}
{
"projectId": "Project:61cd8c0230613d003a66b81d",
"projectToken": "2610971c3349",
"onlyChanged": true
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@qoretechnologies/reqore",
"version": "0.61.0",
"version": "0.62.0",
"description": "ReQore is a highly theme-able and modular UI library for React",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
Loading
Loading