@furystack/shades-common-components
Version:
Common UI components for FuryStack Shades
201 lines • 9.99 kB
JavaScript
import { createInjector } from '@furystack/inject';
import { createComponent, flushUpdates, initializeShadeRoot } from '@furystack/shades';
import { usingAsync } from '@furystack/utils';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { RadioGroup } from './radio-group.js';
import { Radio } from './radio.js';
describe('RadioGroup', () => {
beforeEach(() => {
document.body.innerHTML = '<div id="root"></div>';
});
afterEach(() => {
document.body.innerHTML = '';
vi.restoreAllMocks();
});
it('should render as custom element', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(RadioGroup, { name: "test-group" },
createComponent(Radio, { value: "option1", labelTitle: "Option 1" }),
createComponent(Radio, { value: "option2", labelTitle: "Option 2" }))),
});
await flushUpdates();
const group = document.querySelector('shade-radio-group');
expect(group).not.toBeNull();
});
});
it('should set the radiogroup role', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(RadioGroup, { name: "test-group" },
createComponent(Radio, { value: "option1" }))),
});
await flushUpdates();
const group = document.querySelector('shade-radio-group');
expect(group.getAttribute('role')).toBe('radiogroup');
});
});
it('should render the label title', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(RadioGroup, { name: "test-group", labelTitle: "Pick an option" },
createComponent(Radio, { value: "option1" }))),
});
await flushUpdates();
const label = document.querySelector('shade-radio-group .radio-group-label');
expect(label).not.toBeNull();
expect(label.textContent).toBe('Pick an option');
});
});
it('should not render label span when labelTitle is not provided', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(RadioGroup, { name: "test-group" },
createComponent(Radio, { value: "option1" }))),
});
await flushUpdates();
const label = document.querySelector('shade-radio-group .radio-group-label');
expect(label).toBeNull();
});
});
describe('orientation', () => {
it('should default to vertical orientation', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(RadioGroup, { name: "test-group" },
createComponent(Radio, { value: "option1" }))),
});
await flushUpdates();
const group = document.querySelector('shade-radio-group');
expect(group.getAttribute('data-orientation')).toBe('vertical');
});
});
it('should set horizontal orientation', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(RadioGroup, { name: "test-group", orientation: "horizontal" },
createComponent(Radio, { value: "option1" }))),
});
await flushUpdates();
const group = document.querySelector('shade-radio-group');
expect(group.getAttribute('data-orientation')).toBe('horizontal');
});
});
});
describe('name propagation', () => {
it('should set the name attribute on child radio inputs', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(RadioGroup, { name: "fruit-group" },
createComponent(Radio, { value: "apple", labelTitle: "Apple" }),
createComponent(Radio, { value: "banana", labelTitle: "Banana" }))),
});
await flushUpdates();
const inputs = document.querySelectorAll('shade-radio input[type="radio"]');
inputs.forEach((input) => {
expect(input.name).toBe('fruit-group');
});
});
});
});
describe('value selection', () => {
it('should set the correct radio as checked based on value prop', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(RadioGroup, { name: "test-group", value: "option2" },
createComponent(Radio, { value: "option1", labelTitle: "Option 1" }),
createComponent(Radio, { value: "option2", labelTitle: "Option 2" }),
createComponent(Radio, { value: "option3", labelTitle: "Option 3" }))),
});
await flushUpdates();
const inputs = document.querySelectorAll('shade-radio input[type="radio"]');
expect(inputs[0].checked).toBe(false);
expect(inputs[1].checked).toBe(true);
expect(inputs[2].checked).toBe(false);
});
});
it('should set the correct radio as checked based on defaultValue prop', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(RadioGroup, { name: "test-group", defaultValue: "option3" },
createComponent(Radio, { value: "option1", labelTitle: "Option 1" }),
createComponent(Radio, { value: "option2", labelTitle: "Option 2" }),
createComponent(Radio, { value: "option3", labelTitle: "Option 3" }))),
});
await flushUpdates();
const inputs = document.querySelectorAll('shade-radio input[type="radio"]');
expect(inputs[0].checked).toBe(false);
expect(inputs[1].checked).toBe(false);
expect(inputs[2].checked).toBe(true);
});
});
});
describe('disabled state', () => {
it('should disable all radio inputs when group is disabled', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(RadioGroup, { name: "test-group", disabled: true },
createComponent(Radio, { value: "option1", labelTitle: "Option 1" }),
createComponent(Radio, { value: "option2", labelTitle: "Option 2" }))),
});
await flushUpdates();
const inputs = document.querySelectorAll('shade-radio input[type="radio"]');
inputs.forEach((input) => {
expect(input.disabled).toBe(true);
});
});
});
});
describe('onchange callback', () => {
it('should call onchange when a radio is selected', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onchange = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(RadioGroup, { name: "test-group", onValueChange: onchange },
createComponent(Radio, { value: "option1", labelTitle: "Option 1" }),
createComponent(Radio, { value: "option2", labelTitle: "Option 2" }))),
});
await flushUpdates();
const input = document.querySelectorAll('shade-radio input[type="radio"]')[1];
input.checked = true;
input.dispatchEvent(new Event('change', { bubbles: true }));
await flushUpdates();
expect(onchange).toHaveBeenCalledWith('option2');
});
});
});
});
//# sourceMappingURL=radio-group.spec.js.map