@constela/ui 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/components/accordion/accordion-content.constela.json +20 -0
- package/components/accordion/accordion-item.constela.json +20 -0
- package/components/accordion/accordion-trigger.constela.json +21 -0
- package/components/accordion/accordion.constela.json +18 -0
- package/components/accordion/accordion.styles.json +54 -0
- package/components/accordion/accordion.test.ts +608 -0
- package/components/calendar/calendar.constela.json +195 -0
- package/components/calendar/calendar.styles.json +33 -0
- package/components/calendar/calendar.test.ts +458 -0
- package/components/chart/area-chart.constela.json +482 -0
- package/components/chart/bar-chart.constela.json +342 -0
- package/components/chart/chart-axis.constela.json +224 -0
- package/components/chart/chart-legend.constela.json +82 -0
- package/components/chart/chart-tooltip.constela.json +61 -0
- package/components/chart/chart.styles.json +183 -0
- package/components/chart/chart.test.ts +3260 -0
- package/components/chart/donut-chart.constela.json +369 -0
- package/components/chart/line-chart.constela.json +380 -0
- package/components/chart/pie-chart.constela.json +259 -0
- package/components/chart/radar-chart.constela.json +297 -0
- package/components/chart/scatter-chart.constela.json +300 -0
- package/components/data-table/data-table-cell.constela.json +22 -0
- package/components/data-table/data-table-header.constela.json +30 -0
- package/components/data-table/data-table-pagination.constela.json +19 -0
- package/components/data-table/data-table-row.constela.json +30 -0
- package/components/data-table/data-table.constela.json +32 -0
- package/components/data-table/data-table.styles.json +84 -0
- package/components/data-table/data-table.test.ts +873 -0
- package/components/datepicker/datepicker.constela.json +128 -0
- package/components/datepicker/datepicker.styles.json +47 -0
- package/components/datepicker/datepicker.test.ts +540 -0
- package/components/tree/tree-node.constela.json +26 -0
- package/components/tree/tree.constela.json +24 -0
- package/components/tree/tree.styles.json +50 -0
- package/components/tree/tree.test.ts +542 -0
- package/components/virtual-scroll/virtual-scroll.constela.json +27 -0
- package/components/virtual-scroll/virtual-scroll.styles.json +17 -0
- package/components/virtual-scroll/virtual-scroll.test.ts +345 -0
- package/dist/index.d.ts +1 -2
- package/package.json +3 -3
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
{
|
|
2
|
+
"params": {
|
|
3
|
+
"value": { "type": "string", "required": false },
|
|
4
|
+
"year": { "type": "number", "required": true },
|
|
5
|
+
"month": { "type": "number", "required": true },
|
|
6
|
+
"min": { "type": "string", "required": false },
|
|
7
|
+
"max": { "type": "string", "required": false },
|
|
8
|
+
"disabled": { "type": "boolean", "required": false },
|
|
9
|
+
"locale": { "type": "string", "required": false },
|
|
10
|
+
"weekStartsOn": { "type": "number", "required": false }
|
|
11
|
+
},
|
|
12
|
+
"view": {
|
|
13
|
+
"kind": "element",
|
|
14
|
+
"tag": "div",
|
|
15
|
+
"props": {
|
|
16
|
+
"role": { "expr": "lit", "value": "grid" },
|
|
17
|
+
"aria-label": { "expr": "lit", "value": "Calendar" },
|
|
18
|
+
"className": {
|
|
19
|
+
"expr": "style",
|
|
20
|
+
"preset": "calendarStyles",
|
|
21
|
+
"props": {
|
|
22
|
+
"size": { "expr": "lit", "value": "default" }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"children": [
|
|
27
|
+
{
|
|
28
|
+
"kind": "element",
|
|
29
|
+
"tag": "div",
|
|
30
|
+
"props": {
|
|
31
|
+
"className": {
|
|
32
|
+
"expr": "style",
|
|
33
|
+
"preset": "calendarNavStyles"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"children": [
|
|
37
|
+
{
|
|
38
|
+
"kind": "element",
|
|
39
|
+
"tag": "button",
|
|
40
|
+
"props": {
|
|
41
|
+
"type": { "expr": "lit", "value": "button" },
|
|
42
|
+
"aria-label": { "expr": "lit", "value": "Previous month" },
|
|
43
|
+
"disabled": { "expr": "param", "name": "disabled" }
|
|
44
|
+
},
|
|
45
|
+
"children": [
|
|
46
|
+
{ "kind": "text", "content": { "expr": "lit", "value": "<" } }
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"kind": "element",
|
|
51
|
+
"tag": "span",
|
|
52
|
+
"props": {
|
|
53
|
+
"aria-live": { "expr": "lit", "value": "polite" }
|
|
54
|
+
},
|
|
55
|
+
"children": [
|
|
56
|
+
{
|
|
57
|
+
"kind": "text",
|
|
58
|
+
"content": {
|
|
59
|
+
"expr": "call",
|
|
60
|
+
"fn": "getMonthName",
|
|
61
|
+
"args": [
|
|
62
|
+
{ "expr": "param", "name": "month" },
|
|
63
|
+
{ "expr": "param", "name": "locale" }
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{ "kind": "text", "content": { "expr": "lit", "value": " " } },
|
|
68
|
+
{ "kind": "text", "content": { "expr": "param", "name": "year" } }
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"kind": "element",
|
|
73
|
+
"tag": "button",
|
|
74
|
+
"props": {
|
|
75
|
+
"type": { "expr": "lit", "value": "button" },
|
|
76
|
+
"aria-label": { "expr": "lit", "value": "Next month" },
|
|
77
|
+
"disabled": { "expr": "param", "name": "disabled" }
|
|
78
|
+
},
|
|
79
|
+
"children": [
|
|
80
|
+
{ "kind": "text", "content": { "expr": "lit", "value": ">" } }
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"kind": "element",
|
|
87
|
+
"tag": "div",
|
|
88
|
+
"props": {
|
|
89
|
+
"role": { "expr": "lit", "value": "row" },
|
|
90
|
+
"className": { "expr": "lit", "value": "calendar-weekdays" }
|
|
91
|
+
},
|
|
92
|
+
"children": [
|
|
93
|
+
{
|
|
94
|
+
"kind": "each",
|
|
95
|
+
"items": {
|
|
96
|
+
"expr": "call",
|
|
97
|
+
"fn": "getWeekDays",
|
|
98
|
+
"args": [
|
|
99
|
+
{ "expr": "param", "name": "locale" }
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
"as": "day",
|
|
103
|
+
"body": {
|
|
104
|
+
"kind": "element",
|
|
105
|
+
"tag": "div",
|
|
106
|
+
"props": {
|
|
107
|
+
"role": { "expr": "lit", "value": "columnheader" },
|
|
108
|
+
"aria-label": { "expr": "var", "name": "day" }
|
|
109
|
+
},
|
|
110
|
+
"children": [
|
|
111
|
+
{ "kind": "text", "content": { "expr": "var", "name": "day" } }
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"kind": "element",
|
|
119
|
+
"tag": "div",
|
|
120
|
+
"props": {
|
|
121
|
+
"className": { "expr": "lit", "value": "calendar-grid" }
|
|
122
|
+
},
|
|
123
|
+
"children": [
|
|
124
|
+
{
|
|
125
|
+
"kind": "each",
|
|
126
|
+
"items": {
|
|
127
|
+
"expr": "call",
|
|
128
|
+
"fn": "getCalendarDays",
|
|
129
|
+
"args": [
|
|
130
|
+
{ "expr": "param", "name": "year" },
|
|
131
|
+
{ "expr": "param", "name": "month" }
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
"as": "dayInfo",
|
|
135
|
+
"body": {
|
|
136
|
+
"kind": "element",
|
|
137
|
+
"tag": "button",
|
|
138
|
+
"props": {
|
|
139
|
+
"type": { "expr": "lit", "value": "button" },
|
|
140
|
+
"role": { "expr": "lit", "value": "gridcell" },
|
|
141
|
+
"aria-selected": {
|
|
142
|
+
"expr": "cond",
|
|
143
|
+
"test": {
|
|
144
|
+
"expr": "bin",
|
|
145
|
+
"op": "===",
|
|
146
|
+
"left": { "expr": "param", "name": "value" },
|
|
147
|
+
"right": {
|
|
148
|
+
"expr": "call",
|
|
149
|
+
"fn": "formatDateISO",
|
|
150
|
+
"args": [
|
|
151
|
+
{ "expr": "get", "obj": { "expr": "var", "name": "dayInfo" }, "key": "year" },
|
|
152
|
+
{ "expr": "get", "obj": { "expr": "var", "name": "dayInfo" }, "key": "month" },
|
|
153
|
+
{ "expr": "get", "obj": { "expr": "var", "name": "dayInfo" }, "key": "date" }
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"then": { "expr": "lit", "value": "true" },
|
|
158
|
+
"else": { "expr": "lit", "value": "false" }
|
|
159
|
+
},
|
|
160
|
+
"aria-disabled": {
|
|
161
|
+
"expr": "cond",
|
|
162
|
+
"test": { "expr": "param", "name": "disabled" },
|
|
163
|
+
"then": { "expr": "lit", "value": "true" },
|
|
164
|
+
"else": { "expr": "lit", "value": "false" }
|
|
165
|
+
},
|
|
166
|
+
"disabled": { "expr": "param", "name": "disabled" },
|
|
167
|
+
"className": {
|
|
168
|
+
"expr": "style",
|
|
169
|
+
"preset": "calendarDayStyles",
|
|
170
|
+
"props": {
|
|
171
|
+
"state": {
|
|
172
|
+
"expr": "cond",
|
|
173
|
+
"test": {
|
|
174
|
+
"expr": "not",
|
|
175
|
+
"arg": { "expr": "get", "obj": { "expr": "var", "name": "dayInfo" }, "key": "isCurrentMonth" }
|
|
176
|
+
},
|
|
177
|
+
"then": { "expr": "lit", "value": "outside" },
|
|
178
|
+
"else": { "expr": "lit", "value": "default" }
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
"children": [
|
|
184
|
+
{
|
|
185
|
+
"kind": "text",
|
|
186
|
+
"content": { "expr": "get", "obj": { "expr": "var", "name": "dayInfo" }, "key": "date" }
|
|
187
|
+
}
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
]
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"calendarStyles": {
|
|
3
|
+
"base": "flex flex-col gap-2 p-4 bg-background rounded-lg border border-border",
|
|
4
|
+
"variants": {
|
|
5
|
+
"size": {
|
|
6
|
+
"default": "w-80",
|
|
7
|
+
"sm": "w-64 p-2 gap-1 text-sm",
|
|
8
|
+
"lg": "w-96 p-6 gap-3 text-lg"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"defaultVariants": {
|
|
12
|
+
"size": "default"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"calendarDayStyles": {
|
|
16
|
+
"base": "inline-flex items-center justify-center h-10 w-10 rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
17
|
+
"variants": {
|
|
18
|
+
"state": {
|
|
19
|
+
"default": "hover:bg-accent hover:text-accent-foreground",
|
|
20
|
+
"selected": "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
21
|
+
"today": "bg-accent text-accent-foreground font-bold",
|
|
22
|
+
"disabled": "text-muted-foreground opacity-50 pointer-events-none",
|
|
23
|
+
"outside": "text-muted-foreground opacity-50"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"defaultVariants": {
|
|
27
|
+
"state": "default"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"calendarNavStyles": {
|
|
31
|
+
"base": "flex items-center justify-between px-2 py-1"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test suite for Calendar component
|
|
3
|
+
*
|
|
4
|
+
* @constela/ui Calendar component tests following TDD methodology.
|
|
5
|
+
* These tests verify the Calendar component structure, params, styles, and accessibility.
|
|
6
|
+
*
|
|
7
|
+
* Coverage:
|
|
8
|
+
* - Component structure validation
|
|
9
|
+
* - Params definition validation
|
|
10
|
+
* - Style preset validation
|
|
11
|
+
* - Accessibility attributes
|
|
12
|
+
* - Navigation functionality
|
|
13
|
+
* - Date selection
|
|
14
|
+
* - Locale support
|
|
15
|
+
* - Week start configuration
|
|
16
|
+
* - Disabled state handling
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { describe, it, expect, beforeAll } from 'vitest';
|
|
20
|
+
import {
|
|
21
|
+
loadComponentForTesting,
|
|
22
|
+
assertValidComponent,
|
|
23
|
+
assertValidStylePreset,
|
|
24
|
+
hasParams,
|
|
25
|
+
isOptionalParam,
|
|
26
|
+
hasParamType,
|
|
27
|
+
getRootTag,
|
|
28
|
+
hasVariants,
|
|
29
|
+
hasVariantOptions,
|
|
30
|
+
hasDefaultVariants,
|
|
31
|
+
findPropInView,
|
|
32
|
+
hasRole,
|
|
33
|
+
hasAriaAttribute,
|
|
34
|
+
type ComponentTestContext,
|
|
35
|
+
} from '../../tests/helpers/test-utils.js';
|
|
36
|
+
|
|
37
|
+
describe('Calendar Component', () => {
|
|
38
|
+
let ctx: ComponentTestContext;
|
|
39
|
+
|
|
40
|
+
beforeAll(() => {
|
|
41
|
+
ctx = loadComponentForTesting('calendar');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// ==================== Component Structure Tests ====================
|
|
45
|
+
|
|
46
|
+
describe('Component Structure', () => {
|
|
47
|
+
it('should have valid component structure', () => {
|
|
48
|
+
assertValidComponent(ctx.component);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should have div as root element', () => {
|
|
52
|
+
const rootTag = getRootTag(ctx.component);
|
|
53
|
+
expect(rootTag).toBe('div');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should have role="grid" for calendar grid', () => {
|
|
57
|
+
expect(hasRole(ctx.component.view, 'grid')).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should have className using StyleExpr with calendarStyles preset', () => {
|
|
61
|
+
const className = findPropInView(ctx.component.view, 'className');
|
|
62
|
+
expect(className).not.toBeNull();
|
|
63
|
+
expect(className).toMatchObject({
|
|
64
|
+
expr: 'style',
|
|
65
|
+
preset: 'calendarStyles',
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should have aria-label for accessibility', () => {
|
|
70
|
+
expect(hasAriaAttribute(ctx.component.view, 'label')).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// ==================== Params Validation Tests ====================
|
|
75
|
+
|
|
76
|
+
describe('Params Validation', () => {
|
|
77
|
+
const expectedParams = [
|
|
78
|
+
'value',
|
|
79
|
+
'year',
|
|
80
|
+
'month',
|
|
81
|
+
'min',
|
|
82
|
+
'max',
|
|
83
|
+
'disabled',
|
|
84
|
+
'locale',
|
|
85
|
+
'weekStartsOn',
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
it('should have all expected params', () => {
|
|
89
|
+
expect(hasParams(ctx.component, expectedParams)).toBe(true);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('param: value', () => {
|
|
93
|
+
it('should be optional', () => {
|
|
94
|
+
expect(isOptionalParam(ctx.component, 'value')).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should have type string', () => {
|
|
98
|
+
expect(hasParamType(ctx.component, 'value', 'string')).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('param: year', () => {
|
|
103
|
+
it('should be required', () => {
|
|
104
|
+
expect(isOptionalParam(ctx.component, 'year')).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should have type number', () => {
|
|
108
|
+
expect(hasParamType(ctx.component, 'year', 'number')).toBe(true);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe('param: month', () => {
|
|
113
|
+
it('should be required', () => {
|
|
114
|
+
expect(isOptionalParam(ctx.component, 'month')).toBe(false);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should have type number', () => {
|
|
118
|
+
expect(hasParamType(ctx.component, 'month', 'number')).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('param: min', () => {
|
|
123
|
+
it('should be optional', () => {
|
|
124
|
+
expect(isOptionalParam(ctx.component, 'min')).toBe(true);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should have type string', () => {
|
|
128
|
+
expect(hasParamType(ctx.component, 'min', 'string')).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('param: max', () => {
|
|
133
|
+
it('should be optional', () => {
|
|
134
|
+
expect(isOptionalParam(ctx.component, 'max')).toBe(true);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should have type string', () => {
|
|
138
|
+
expect(hasParamType(ctx.component, 'max', 'string')).toBe(true);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe('param: disabled', () => {
|
|
143
|
+
it('should be optional', () => {
|
|
144
|
+
expect(isOptionalParam(ctx.component, 'disabled')).toBe(true);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should have type boolean', () => {
|
|
148
|
+
expect(hasParamType(ctx.component, 'disabled', 'boolean')).toBe(true);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('param: locale', () => {
|
|
153
|
+
it('should be optional', () => {
|
|
154
|
+
expect(isOptionalParam(ctx.component, 'locale')).toBe(true);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should have type string', () => {
|
|
158
|
+
expect(hasParamType(ctx.component, 'locale', 'string')).toBe(true);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('param: weekStartsOn', () => {
|
|
163
|
+
it('should be optional', () => {
|
|
164
|
+
expect(isOptionalParam(ctx.component, 'weekStartsOn')).toBe(true);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should have type number', () => {
|
|
168
|
+
expect(hasParamType(ctx.component, 'weekStartsOn', 'number')).toBe(true);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// ==================== Style Preset Tests ====================
|
|
174
|
+
|
|
175
|
+
describe('Style Preset', () => {
|
|
176
|
+
it('should have valid style preset structure', () => {
|
|
177
|
+
const calendarStyles = ctx.styles['calendarStyles'];
|
|
178
|
+
expect(calendarStyles).toBeDefined();
|
|
179
|
+
assertValidStylePreset(calendarStyles);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should have base classes for common calendar styles', () => {
|
|
183
|
+
const calendarStyles = ctx.styles['calendarStyles'];
|
|
184
|
+
expect(calendarStyles.base).toBeDefined();
|
|
185
|
+
expect(typeof calendarStyles.base).toBe('string');
|
|
186
|
+
expect(calendarStyles.base.length).toBeGreaterThan(0);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe('size variants', () => {
|
|
190
|
+
const sizeOptions = ['default', 'sm', 'lg'];
|
|
191
|
+
|
|
192
|
+
it('should have size variants', () => {
|
|
193
|
+
const calendarStyles = ctx.styles['calendarStyles'];
|
|
194
|
+
expect(hasVariants(calendarStyles, ['size'])).toBe(true);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it.each(sizeOptions)('should have %s size option', (option) => {
|
|
198
|
+
const calendarStyles = ctx.styles['calendarStyles'];
|
|
199
|
+
expect(hasVariantOptions(calendarStyles, 'size', [option])).toBe(true);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
describe('default variants', () => {
|
|
204
|
+
it('should have default size set to default', () => {
|
|
205
|
+
const calendarStyles = ctx.styles['calendarStyles'];
|
|
206
|
+
expect(hasDefaultVariants(calendarStyles, { size: 'default' })).toBe(true);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// ==================== Calendar Day Styles ====================
|
|
212
|
+
|
|
213
|
+
describe('Calendar Day Styles', () => {
|
|
214
|
+
it('should have calendarDayStyles preset', () => {
|
|
215
|
+
const calendarDayStyles = ctx.styles['calendarDayStyles'];
|
|
216
|
+
expect(calendarDayStyles).toBeDefined();
|
|
217
|
+
assertValidStylePreset(calendarDayStyles);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('should have base classes for day cells', () => {
|
|
221
|
+
const calendarDayStyles = ctx.styles['calendarDayStyles'];
|
|
222
|
+
expect(calendarDayStyles.base).toBeDefined();
|
|
223
|
+
expect(typeof calendarDayStyles.base).toBe('string');
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
describe('day state variants', () => {
|
|
227
|
+
const stateOptions = ['default', 'selected', 'today', 'disabled', 'outside'];
|
|
228
|
+
|
|
229
|
+
it('should have state variants', () => {
|
|
230
|
+
const calendarDayStyles = ctx.styles['calendarDayStyles'];
|
|
231
|
+
expect(hasVariants(calendarDayStyles, ['state'])).toBe(true);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it.each(stateOptions)('should have %s state option', (option) => {
|
|
235
|
+
const calendarDayStyles = ctx.styles['calendarDayStyles'];
|
|
236
|
+
expect(hasVariantOptions(calendarDayStyles, 'state', [option])).toBe(true);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// ==================== Calendar Navigation Styles ====================
|
|
242
|
+
|
|
243
|
+
describe('Calendar Navigation Styles', () => {
|
|
244
|
+
it('should have calendarNavStyles preset', () => {
|
|
245
|
+
const calendarNavStyles = ctx.styles['calendarNavStyles'];
|
|
246
|
+
expect(calendarNavStyles).toBeDefined();
|
|
247
|
+
assertValidStylePreset(calendarNavStyles);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('should have base classes for navigation', () => {
|
|
251
|
+
const calendarNavStyles = ctx.styles['calendarNavStyles'];
|
|
252
|
+
expect(calendarNavStyles.base).toBeDefined();
|
|
253
|
+
expect(typeof calendarNavStyles.base).toBe('string');
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// ==================== Rendering Tests ====================
|
|
258
|
+
|
|
259
|
+
describe('Rendering', () => {
|
|
260
|
+
it('should render calendar grid with correct structure', () => {
|
|
261
|
+
// Calendar should have header (navigation), weekday headers, and day grid
|
|
262
|
+
const view = ctx.component.view;
|
|
263
|
+
expect(view.kind).toBe('element');
|
|
264
|
+
if (view.kind === 'element') {
|
|
265
|
+
expect(view.children).toBeDefined();
|
|
266
|
+
expect(view.children!.length).toBeGreaterThan(0);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('should display month name in header', () => {
|
|
271
|
+
// Component should have a text node or element displaying month/year
|
|
272
|
+
const view = ctx.component.view;
|
|
273
|
+
expect(view).toBeDefined();
|
|
274
|
+
// The month display should reference year and month params
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('should show weekday headers', () => {
|
|
278
|
+
// Should have 7 weekday header elements
|
|
279
|
+
const view = ctx.component.view;
|
|
280
|
+
expect(view).toBeDefined();
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('should highlight selected date when value is provided', () => {
|
|
284
|
+
// Day with matching value should have 'selected' state
|
|
285
|
+
const view = ctx.component.view;
|
|
286
|
+
expect(view).toBeDefined();
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// ==================== Navigation Tests ====================
|
|
291
|
+
|
|
292
|
+
describe('Navigation', () => {
|
|
293
|
+
it('should have previous month navigation button', () => {
|
|
294
|
+
const view = ctx.component.view;
|
|
295
|
+
// Should find a button for previous month navigation
|
|
296
|
+
expect(view).toBeDefined();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('should have next month navigation button', () => {
|
|
300
|
+
const view = ctx.component.view;
|
|
301
|
+
// Should find a button for next month navigation
|
|
302
|
+
expect(view).toBeDefined();
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('should support year transition from December to January', () => {
|
|
306
|
+
// Navigation from month 11 (Dec) should go to month 0 (Jan) of next year
|
|
307
|
+
const view = ctx.component.view;
|
|
308
|
+
expect(view).toBeDefined();
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('should support year transition from January to December', () => {
|
|
312
|
+
// Navigation from month 0 (Jan) should go to month 11 (Dec) of previous year
|
|
313
|
+
const view = ctx.component.view;
|
|
314
|
+
expect(view).toBeDefined();
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// ==================== Date Selection Tests ====================
|
|
319
|
+
|
|
320
|
+
describe('Date Selection', () => {
|
|
321
|
+
it('should have event handler for date selection', () => {
|
|
322
|
+
// Day cells should have onClick or similar event for @select
|
|
323
|
+
const view = ctx.component.view;
|
|
324
|
+
expect(view).toBeDefined();
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('should not allow selection of disabled dates', () => {
|
|
328
|
+
// Dates outside min/max range or when disabled=true should not trigger @select
|
|
329
|
+
const view = ctx.component.view;
|
|
330
|
+
expect(view).toBeDefined();
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it('should respect min date boundary', () => {
|
|
334
|
+
// Dates before min should be marked as disabled
|
|
335
|
+
const view = ctx.component.view;
|
|
336
|
+
expect(view).toBeDefined();
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('should respect max date boundary', () => {
|
|
340
|
+
// Dates after max should be marked as disabled
|
|
341
|
+
const view = ctx.component.view;
|
|
342
|
+
expect(view).toBeDefined();
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// ==================== Locale Support Tests ====================
|
|
347
|
+
|
|
348
|
+
describe('Locale Support', () => {
|
|
349
|
+
it('should support locale param for month names', () => {
|
|
350
|
+
const localeParam = ctx.component.params?.['locale'];
|
|
351
|
+
expect(localeParam).toBeDefined();
|
|
352
|
+
expect(localeParam?.type).toBe('string');
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('should support locale param for weekday names', () => {
|
|
356
|
+
// Weekday names should be localizable based on locale param
|
|
357
|
+
const view = ctx.component.view;
|
|
358
|
+
expect(view).toBeDefined();
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// ==================== Week Start Configuration Tests ====================
|
|
363
|
+
|
|
364
|
+
describe('Week Start Configuration', () => {
|
|
365
|
+
it('should support weekStartsOn param', () => {
|
|
366
|
+
const weekStartsOnParam = ctx.component.params?.['weekStartsOn'];
|
|
367
|
+
expect(weekStartsOnParam).toBeDefined();
|
|
368
|
+
expect(weekStartsOnParam?.type).toBe('number');
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it('should default to Sunday first (weekStartsOn: 0)', () => {
|
|
372
|
+
const weekStartsOnParam = ctx.component.params?.['weekStartsOn'];
|
|
373
|
+
expect(weekStartsOnParam?.required).toBe(false);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it('should support Monday first (weekStartsOn: 1)', () => {
|
|
377
|
+
// Component should handle weekStartsOn: 1 to show Monday as first day
|
|
378
|
+
const view = ctx.component.view;
|
|
379
|
+
expect(view).toBeDefined();
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
// ==================== Disabled State Tests ====================
|
|
384
|
+
|
|
385
|
+
describe('Disabled State', () => {
|
|
386
|
+
it('should support disabled param', () => {
|
|
387
|
+
const disabledParam = ctx.component.params?.['disabled'];
|
|
388
|
+
expect(disabledParam).toBeDefined();
|
|
389
|
+
expect(disabledParam?.type).toBe('boolean');
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('should prevent all interactions when disabled is true', () => {
|
|
393
|
+
// When disabled=true, navigation and date selection should be blocked
|
|
394
|
+
const view = ctx.component.view;
|
|
395
|
+
expect(view).toBeDefined();
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it('should apply disabled styling when disabled is true', () => {
|
|
399
|
+
// Calendar should have visual indication of disabled state
|
|
400
|
+
const view = ctx.component.view;
|
|
401
|
+
expect(view).toBeDefined();
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
// ==================== Accessibility Tests ====================
|
|
406
|
+
|
|
407
|
+
describe('Accessibility', () => {
|
|
408
|
+
it('should have proper ARIA role for calendar grid', () => {
|
|
409
|
+
expect(hasRole(ctx.component.view, 'grid')).toBe(true);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it('should have aria-label for calendar', () => {
|
|
413
|
+
expect(hasAriaAttribute(ctx.component.view, 'label')).toBe(true);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it('should have accessible navigation buttons', () => {
|
|
417
|
+
// Navigation buttons should have aria-label
|
|
418
|
+
const view = ctx.component.view;
|
|
419
|
+
expect(view).toBeDefined();
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('should mark disabled dates with aria-disabled', () => {
|
|
423
|
+
// Disabled dates should have aria-disabled="true"
|
|
424
|
+
const view = ctx.component.view;
|
|
425
|
+
expect(view).toBeDefined();
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('should mark selected date with aria-selected', () => {
|
|
429
|
+
// Selected date should have aria-selected="true"
|
|
430
|
+
const view = ctx.component.view;
|
|
431
|
+
expect(view).toBeDefined();
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
// ==================== View Props Tests ====================
|
|
436
|
+
|
|
437
|
+
describe('View Props', () => {
|
|
438
|
+
it('should pass year to component', () => {
|
|
439
|
+
const params = ctx.component.params;
|
|
440
|
+
expect(params?.['year']).toBeDefined();
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it('should pass month to component', () => {
|
|
444
|
+
const params = ctx.component.params;
|
|
445
|
+
expect(params?.['month']).toBeDefined();
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it('should pass value to component for selected date', () => {
|
|
449
|
+
const params = ctx.component.params;
|
|
450
|
+
expect(params?.['value']).toBeDefined();
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it('should pass disabled to component', () => {
|
|
454
|
+
const params = ctx.component.params;
|
|
455
|
+
expect(params?.['disabled']).toBeDefined();
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
});
|