native-document 1.0.169 → 1.0.172
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/CHANGELOG.md +62 -1
- package/docs/vitepress-conventions.md +2 -2
- package/package.json +11 -4
- package/src/components/BaseComponent.js +43 -1
- package/src/components/accordion/Accordion.js +1 -0
- package/src/components/accordion/AccordionItem.js +1 -0
- package/src/components/alert/Alert.js +4 -0
- package/src/components/avatar/Avatar.js +1 -0
- package/src/components/avatar/AvatarGroup.js +1 -0
- package/src/components/badge/Badge.js +1 -0
- package/src/components/breadcrumb/BreadCrumb.js +4 -0
- package/src/components/button/Button.js +1 -0
- package/src/components/card/Card.js +1 -0
- package/src/components/context-menu/ContextMenu.js +2 -0
- package/src/components/context-menu/ContextMenuGroup.js +2 -0
- package/src/components/context-menu/ContextMenuItem.js +2 -0
- package/src/components/divider/Divider.js +2 -0
- package/src/components/dropdown/Dropdown.js +1 -0
- package/src/components/dropdown/DropdownDivider.js +1 -0
- package/src/components/dropdown/DropdownGroup.js +1 -0
- package/src/components/dropdown/DropdownItem.js +1 -0
- package/src/components/dropdown/DropdownTrigger.js +1 -0
- package/src/components/form/FormControl.js +1 -0
- package/src/components/form/field/Field.js +1 -0
- package/src/components/form/field/types/StringField.js +1 -1
- package/src/components/icon/Icon.js +1 -0
- package/src/components/list/ListItem.js +1 -0
- package/src/components/menu/Menu.js +3 -0
- package/src/components/menu/MenuDivider.js +1 -0
- package/src/components/menu/MenuGroup.js +1 -0
- package/src/components/menu/MenuItem.js +1 -0
- package/src/components/menu/MenuLink.js +1 -0
- package/src/components/modal/Modal.js +4 -0
- package/src/components/pagination/Pagination.js +1 -0
- package/src/components/popover/Popover.js +1 -0
- package/src/components/popover/PopoverFooter.js +1 -0
- package/src/components/popover/PopoverHeader.js +1 -0
- package/src/components/progress/Progress.js +5 -0
- package/src/components/skeleton/Skeleton.js +4 -0
- package/src/components/slider/Slider.js +1 -0
- package/src/components/spacer/Spacer.js +1 -0
- package/src/components/spinner/Spinner.js +1 -0
- package/src/components/splitter/Splitter.js +1 -0
- package/src/components/splitter/SplitterGutter.js +6 -0
- package/src/components/splitter/SplitterPanel.js +1 -0
- package/src/components/stacks/AbsoluteStack.js +1 -0
- package/src/components/stacks/FixedStack.js +1 -0
- package/src/components/stacks/HStack.js +1 -0
- package/src/components/stacks/PositionStack.js +1 -0
- package/src/components/stacks/RelativeStack.js +1 -0
- package/src/components/stacks/Stack.js +1 -0
- package/src/components/stacks/VStack.js +1 -0
- package/src/components/stepper/Stepper.js +1 -0
- package/src/components/stepper/StepperStep.js +1 -0
- package/src/components/switch/Switch.js +1 -0
- package/src/components/table/DataTable.js +1 -0
- package/src/components/table/SimpleTable.js +1 -0
- package/src/components/tabs/Tabs.js +1 -0
- package/src/components/toast/Toast.js +1 -0
- package/src/components/tooltip/Tooltip.js +1 -0
- package/src/core/data/MemoryManager.js +6 -1
- package/src/core/data/ObservableItem.js +28 -10
- package/src/core/elements/anchor/anchor.js +4 -3
- package/src/core/elements/anchor/one-child-anchor-overwriting.js +4 -4
- package/src/core/elements/content-formatter.js +19 -0
- package/src/core/elements/control/for-each-array-cache.js +59 -0
- package/src/core/elements/control/for-each-array.js +15 -23
- package/src/core/wrappers/AttributesWrapper.js +12 -12
- package/src/core/wrappers/ElementCreator.js +33 -10
- package/src/core/wrappers/HtmlElementWrapper.js +9 -4
- package/src/core/wrappers/NDElement.js +23 -8
- package/src/core/wrappers/NdPrototype.js +31 -39
- package/src/core/wrappers/constants.js +12 -12
- package/src/core/wrappers/prototypes/nd-element-extensions.js +127 -57
- package/src/ui/components/accordion/AccordionItemRender.js +29 -7
- package/src/ui/components/alert/AlertRender.js +6 -0
- package/src/ui/components/breadcrumb/BreadcrumbRender.js +9 -1
- package/src/ui/components/button/ButtonRender.js +3 -0
- package/src/ui/components/contextmenu/ContextmenuRender.js +2 -0
- package/src/ui/components/dropdown/DropdownRender.js +3 -0
- package/src/ui/components/dropdown/item/DropdownItemRender.js +6 -1
- package/src/ui/components/form/FieldCollectionRender.js +4 -0
- package/src/ui/components/form/fields/CheckboxFieldRender.js +4 -0
- package/src/ui/components/form/fields/CheckboxGroupFieldRender.js +4 -0
- package/src/ui/components/form/fields/RadioFieldRender.js +4 -0
- package/src/ui/components/form/fields/RangeFieldRender.js +4 -0
- package/src/ui/components/form/fields/SelectFieldRender.js +5 -0
- package/src/ui/components/form/fields/SliderFieldRender.js +3 -0
- package/src/ui/components/icon/tabler/TablerIconRender.js +0 -1
- package/src/ui/components/list/item/ListItemRender.js +4 -0
- package/src/ui/components/menu/MenuItemRender.js +8 -0
- package/src/ui/components/menu/MenuLinkRender.js +11 -0
- package/src/ui/components/menu/MenuRender.js +5 -0
- package/src/ui/components/menu/helpers.js +2 -1
- package/src/ui/components/modal/ModalRender.js +8 -2
- package/src/ui/components/progress/ProgressRender.js +9 -0
- package/src/ui/components/skeleton/SkeletonRender.js +2 -0
- package/src/ui/components/spinner/SpinnerRender.js +3 -0
- package/src/ui/components/splitter/SplitterGutterRender.js +4 -0
- package/src/ui/components/splitter/SplitterPanelRender.js +4 -0
- package/src/ui/components/splitter/SplitterRender.js +2 -0
- package/src/ui/components/stepper/StepperRender.js +2 -1
- package/src/ui/components/stepper/StepperStepRender.js +8 -1
- package/src/ui/components/switch/SwitchRender.js +3 -0
- package/src/ui/components/table/data-table/DataTableRender.js +4 -0
- package/src/ui/components/table/simple-table/SimpleTableRender.js +2 -0
- package/src/ui/components/toast/ToastRender.js +4 -0
- package/src/ui/utils/aria.js +19 -0
- package/tests/integration/ui/attributes-wrapper.test.js +177 -0
- package/tests/integration/ui/for-each-array.test.js +167 -0
- package/tests/integration/ui/lifecycle.test.js +146 -0
- package/tests/integration/ui/show-if.test.js +168 -0
- package/tests/unit/core/observable-array.test.js +404 -0
- package/tests/unit/core/observable-item.test.js +428 -0
- package/tests/unit/core/observable-object.test.js +239 -0
- package/tests/unit/core/observable-resource.test.js +323 -0
- package/tests/unit/core/store.test.js +347 -0
- package/tests/unit/router/route-matching.test.js +169 -0
- package/tests/unit/router/router-guards.test.js +189 -0
- package/tests/unit/utils/cache.test.js +120 -0
- package/tests/unit/utils/filters.test.js +394 -0
- package/types/elements.d.ts +5 -0
- package/vitest.config.js +15 -0
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
vi.mock('../../../src/core/utils/debug-manager', () => ({
|
|
4
|
+
default: { log: vi.fn(), warn: vi.fn(), error: vi.fn(), disable: vi.fn() },
|
|
5
|
+
}));
|
|
6
|
+
import { Observable } from '../../../src/core/data/Observable';
|
|
7
|
+
import {
|
|
8
|
+
equals, notEquals,
|
|
9
|
+
greaterThan, greaterThanOrEqual,
|
|
10
|
+
lessThan, lessThanOrEqual,
|
|
11
|
+
between,
|
|
12
|
+
inArray, notIn,
|
|
13
|
+
isEmpty, isNotEmpty,
|
|
14
|
+
match,
|
|
15
|
+
and, or, not,
|
|
16
|
+
custom,
|
|
17
|
+
gt, gte, lt, lte, eq, neq,
|
|
18
|
+
} from '../../../src/core/utils/filters/standard';
|
|
19
|
+
import {
|
|
20
|
+
includes, contains, startsWith, endsWith,
|
|
21
|
+
} from '../../../src/core/utils/filters/strings';
|
|
22
|
+
import {
|
|
23
|
+
dateEquals, dateBefore, dateAfter, dateBetween,
|
|
24
|
+
timeEquals, timeAfter, timeBefore, timeBetween,
|
|
25
|
+
dateTimeEquals, dateTimeAfter, dateTimeBefore, dateTimeBetween,
|
|
26
|
+
} from '../../../src/core/utils/filters/date';
|
|
27
|
+
import { createFilter, createMultiSourceFilter } from '../../../src/core/utils/filters/utils';
|
|
28
|
+
|
|
29
|
+
const run = (filter, value) => filter.callback(value);
|
|
30
|
+
|
|
31
|
+
describe('Filters', () => {
|
|
32
|
+
|
|
33
|
+
// -- equals / notEquals ---------------------------------------------------
|
|
34
|
+
|
|
35
|
+
describe('equals() / notEquals()', () => {
|
|
36
|
+
it('equals passes when values match', () => {
|
|
37
|
+
expect(run(equals('active'), 'active')).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('equals fails when values differ', () => {
|
|
41
|
+
expect(run(equals('active'), 'inactive')).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('notEquals passes when values differ', () => {
|
|
45
|
+
expect(run(notEquals('deleted'), 'active')).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('notEquals fails when values match', () => {
|
|
49
|
+
expect(run(notEquals('deleted'), 'deleted')).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('eq is an alias for equals', () => {
|
|
53
|
+
expect(run(eq(1), 1)).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('neq is an alias for notEquals', () => {
|
|
57
|
+
expect(run(neq(1), 2)).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('equals with Observable target', () => {
|
|
61
|
+
const status = Observable('active');
|
|
62
|
+
const filter = equals(status);
|
|
63
|
+
expect(run(filter, 'active')).toBe(true);
|
|
64
|
+
status.set('inactive');
|
|
65
|
+
expect(run(filter, 'active')).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// -- greaterThan / lessThan -----------------------------------------------
|
|
70
|
+
|
|
71
|
+
describe('greaterThan / lessThan / between', () => {
|
|
72
|
+
it('greaterThan passes when value > target', () => {
|
|
73
|
+
expect(run(greaterThan(5), 6)).toBe(true);
|
|
74
|
+
expect(run(greaterThan(5), 5)).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('greaterThanOrEqual passes when value >= target', () => {
|
|
78
|
+
expect(run(greaterThanOrEqual(5), 5)).toBe(true);
|
|
79
|
+
expect(run(greaterThanOrEqual(5), 4)).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('lessThan passes when value < target', () => {
|
|
83
|
+
expect(run(lessThan(5), 4)).toBe(true);
|
|
84
|
+
expect(run(lessThan(5), 5)).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('lessThanOrEqual passes when value <= target', () => {
|
|
88
|
+
expect(run(lessThanOrEqual(5), 5)).toBe(true);
|
|
89
|
+
expect(run(lessThanOrEqual(5), 6)).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('between passes when value is within range (inclusive)', () => {
|
|
93
|
+
expect(run(between(1, 10), 5)).toBe(true);
|
|
94
|
+
expect(run(between(1, 10), 1)).toBe(true);
|
|
95
|
+
expect(run(between(1, 10), 10)).toBe(true);
|
|
96
|
+
expect(run(between(1, 10), 11)).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('gt, gte, lt, lte are aliases', () => {
|
|
100
|
+
expect(run(gt(5), 6)).toBe(true);
|
|
101
|
+
expect(run(gte(5), 5)).toBe(true);
|
|
102
|
+
expect(run(lt(5), 4)).toBe(true);
|
|
103
|
+
expect(run(lte(5), 5)).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// -- inArray / notIn ------------------------------------------------------
|
|
108
|
+
|
|
109
|
+
describe('inArray() / notIn()', () => {
|
|
110
|
+
it('inArray passes when value is in the array', () => {
|
|
111
|
+
expect(run(inArray(['a', 'b', 'c']), 'b')).toBe(true);
|
|
112
|
+
expect(run(inArray(['a', 'b', 'c']), 'd')).toBe(false);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('notIn passes when value is not in the array', () => {
|
|
116
|
+
expect(run(notIn(['a', 'b']), 'c')).toBe(true);
|
|
117
|
+
expect(run(notIn(['a', 'b']), 'a')).toBe(false);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('inArray with Observable array', () => {
|
|
121
|
+
const allowed = Observable(['admin', 'editor']);
|
|
122
|
+
const filter = inArray(allowed);
|
|
123
|
+
expect(run(filter, 'admin')).toBe(true);
|
|
124
|
+
allowed.set(['viewer']);
|
|
125
|
+
expect(run(filter, 'admin')).toBe(false);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// -- isEmpty / isNotEmpty -------------------------------------------------
|
|
130
|
+
|
|
131
|
+
describe('isEmpty() / isNotEmpty()', () => {
|
|
132
|
+
it('isEmpty passes for null', () => {
|
|
133
|
+
expect(run(isEmpty(), null)).toBe(true);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('isEmpty passes for empty string', () => {
|
|
137
|
+
expect(run(isEmpty(), '')).toBe(true);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('isEmpty passes for empty array', () => {
|
|
141
|
+
expect(run(isEmpty(), [])).toBe(true);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('isEmpty fails for non-empty value', () => {
|
|
145
|
+
expect(run(isEmpty(), 'hello')).toBe(false);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('isEmpty(false) passes for non-empty value', () => {
|
|
149
|
+
expect(run(isEmpty(false), 'hello')).toBe(true);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('isNotEmpty passes for non-empty string', () => {
|
|
153
|
+
expect(run(isNotEmpty(), 'hello')).toBe(true);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('isNotEmpty fails for null', () => {
|
|
157
|
+
expect(run(isNotEmpty(), null)).toBe(false);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// -- match() --------------------------------------------------------------
|
|
162
|
+
|
|
163
|
+
describe('match()', () => {
|
|
164
|
+
it('matches with regex pattern', () => {
|
|
165
|
+
expect(run(match('^hello'), 'hello world')).toBe(true);
|
|
166
|
+
expect(run(match('^world'), 'hello world')).toBe(false);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('passes when pattern is empty', () => {
|
|
170
|
+
expect(run(match(''), 'anything')).toBe(true);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('substring match when asRegex=false', () => {
|
|
174
|
+
expect(run(match('john', false), 'John Doe')).toBe(true);
|
|
175
|
+
expect(run(match('jane', false), 'John Doe')).toBe(false);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('supports regex flags', () => {
|
|
179
|
+
expect(run(match('HELLO', true, 'i'), 'hello world')).toBe(true);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('returns false for invalid regex (does not throw)', () => {
|
|
183
|
+
expect(run(match('[invalid'), 'test')).toBe(false);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// -- and() / or() / not() -------------------------------------------------
|
|
188
|
+
|
|
189
|
+
describe('and() / or() / not()', () => {
|
|
190
|
+
it('and passes when all filters pass', () => {
|
|
191
|
+
const f = and(greaterThan(0), lessThan(10));
|
|
192
|
+
expect(run(f, 5)).toBe(true);
|
|
193
|
+
expect(run(f, 10)).toBe(false);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('and fails when any filter fails', () => {
|
|
197
|
+
const f = and(greaterThan(0), lessThan(10));
|
|
198
|
+
expect(run(f, -1)).toBe(false);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('or passes when at least one filter passes', () => {
|
|
202
|
+
const f = or(equals('admin'), equals('editor'));
|
|
203
|
+
expect(run(f, 'editor')).toBe(true);
|
|
204
|
+
expect(run(f, 'viewer')).toBe(false);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('not negates a filter', () => {
|
|
208
|
+
const f = not(equals('deleted'));
|
|
209
|
+
expect(run(f, 'active')).toBe(true);
|
|
210
|
+
expect(run(f, 'deleted')).toBe(false);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('and merges dependencies from child filters', () => {
|
|
214
|
+
const obs = Observable(5);
|
|
215
|
+
const f = and(greaterThan(obs), lessThan(100));
|
|
216
|
+
expect(f.dependencies).not.toBeNull();
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('or merges dependencies from child filters', () => {
|
|
220
|
+
const obs = Observable('admin');
|
|
221
|
+
const f = or(equals(obs), equals('editor'));
|
|
222
|
+
expect(f.dependencies).not.toBeNull();
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// -- custom() -------------------------------------------------------------
|
|
227
|
+
|
|
228
|
+
describe('custom()', () => {
|
|
229
|
+
it('passes custom callback result', () => {
|
|
230
|
+
const f = custom((value) => value > 10);
|
|
231
|
+
expect(run(f, 15)).toBe(true);
|
|
232
|
+
expect(run(f, 5)).toBe(false);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('receives observable values as extra args', () => {
|
|
236
|
+
const min = Observable(5);
|
|
237
|
+
const f = custom((value, minVal) => value >= minVal, min);
|
|
238
|
+
expect(run(f, 5)).toBe(true);
|
|
239
|
+
min.set(10);
|
|
240
|
+
expect(run(f, 5)).toBe(false);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('has dependencies when observables are passed', () => {
|
|
244
|
+
const obs = Observable(1);
|
|
245
|
+
const f = custom((v) => v > 0, obs);
|
|
246
|
+
expect(f.dependencies).toContain(obs);
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// -- strings: includes / startsWith / endsWith ----------------------------
|
|
251
|
+
|
|
252
|
+
describe('String filters', () => {
|
|
253
|
+
it('includes passes for substring (case-insensitive)', () => {
|
|
254
|
+
expect(run(includes('john'), 'John Doe')).toBe(true);
|
|
255
|
+
expect(run(includes('jane'), 'John Doe')).toBe(false);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('includes case-sensitive mode', () => {
|
|
259
|
+
expect(run(includes('John', true), 'John Doe')).toBe(true);
|
|
260
|
+
expect(run(includes('john', true), 'John Doe')).toBe(false);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('contains is an alias for includes', () => {
|
|
264
|
+
expect(run(contains('john'), 'John Doe')).toBe(true);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('startsWith passes for matching prefix', () => {
|
|
268
|
+
expect(run(startsWith('Jo'), 'John Doe')).toBe(true);
|
|
269
|
+
expect(run(startsWith('Do'), 'John Doe')).toBe(false);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('endsWith passes for matching suffix', () => {
|
|
273
|
+
expect(run(endsWith('.js'), 'index.js')).toBe(true);
|
|
274
|
+
expect(run(endsWith('.ts'), 'index.js')).toBe(false);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('startsWith passes when query is empty', () => {
|
|
278
|
+
expect(run(startsWith(''), 'anything')).toBe(true);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('endsWith passes when query is empty', () => {
|
|
282
|
+
expect(run(endsWith(''), 'anything')).toBe(true);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// -- date filters ---------------------------------------------------------
|
|
287
|
+
|
|
288
|
+
describe('Date filters', () => {
|
|
289
|
+
const d1 = new Date('2024-01-15');
|
|
290
|
+
const d2 = new Date('2024-01-20');
|
|
291
|
+
const d3 = new Date('2024-01-25');
|
|
292
|
+
|
|
293
|
+
it('dateEquals passes on same day', () => {
|
|
294
|
+
expect(run(dateEquals(d2), new Date('2024-01-20'))).toBe(true);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('dateEquals fails on different day', () => {
|
|
298
|
+
expect(run(dateEquals(d2), d1)).toBe(false);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('dateBefore passes when value is before target', () => {
|
|
302
|
+
expect(run(dateBefore(d2), d1)).toBe(true);
|
|
303
|
+
expect(run(dateBefore(d2), d3)).toBe(false);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('dateAfter passes when value is after target', () => {
|
|
307
|
+
expect(run(dateAfter(d2), d3)).toBe(true);
|
|
308
|
+
expect(run(dateAfter(d2), d1)).toBe(false);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('dateBetween passes when value is within range', () => {
|
|
312
|
+
expect(run(dateBetween(d1, d3), d2)).toBe(true);
|
|
313
|
+
expect(run(dateBetween(d1, d2), d3)).toBe(false);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('dateEquals fails for null value', () => {
|
|
317
|
+
expect(run(dateEquals(d1), null)).toBe(false);
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// -- time filters ---------------------------------------------------------
|
|
322
|
+
|
|
323
|
+
describe('Time filters', () => {
|
|
324
|
+
const t1 = new Date('2024-01-01T08:00:00');
|
|
325
|
+
const t2 = new Date('2024-01-01T12:00:00');
|
|
326
|
+
const t3 = new Date('2024-01-01T18:00:00');
|
|
327
|
+
|
|
328
|
+
it('timeEquals passes for same time', () => {
|
|
329
|
+
expect(run(timeEquals(t2), new Date('2024-01-01T12:00:00'))).toBe(true);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it('timeBefore passes when value is before target', () => {
|
|
333
|
+
expect(run(timeBefore(t2), t1)).toBe(true);
|
|
334
|
+
expect(run(timeBefore(t2), t3)).toBe(false);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('timeAfter passes when value is after target', () => {
|
|
338
|
+
expect(run(timeAfter(t2), t3)).toBe(true);
|
|
339
|
+
expect(run(timeAfter(t2), t1)).toBe(false);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('timeBetween passes when value is within range', () => {
|
|
343
|
+
expect(run(timeBetween(t1, t3), t2)).toBe(true);
|
|
344
|
+
expect(run(timeBetween(t1, t2), t3)).toBe(false);
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// -- createFilter / createMultiSourceFilter utils -------------------------
|
|
349
|
+
|
|
350
|
+
describe('createFilter()', () => {
|
|
351
|
+
it('creates a filter with null dependencies for static value', () => {
|
|
352
|
+
const f = createFilter('active', (v, t) => v === t);
|
|
353
|
+
expect(f.dependencies).toBeNull();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('creates a filter with observable dependency', () => {
|
|
357
|
+
const obs = Observable('active');
|
|
358
|
+
const f = createFilter(obs, (v, t) => v === t);
|
|
359
|
+
expect(f.dependencies).toBe(obs);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it('callback uses the current observable value', () => {
|
|
363
|
+
const obs = Observable('active');
|
|
364
|
+
const f = createFilter(obs, (v, t) => v === t);
|
|
365
|
+
expect(f.callback('active')).toBe(true);
|
|
366
|
+
obs.set('inactive');
|
|
367
|
+
expect(f.callback('active')).toBe(false);
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
describe('createMultiSourceFilter()', () => {
|
|
372
|
+
it('creates a filter with multiple observable dependencies', () => {
|
|
373
|
+
const min = Observable(0);
|
|
374
|
+
const max = Observable(10);
|
|
375
|
+
const f = createMultiSourceFilter([min, max], (v, [a, b]) => v >= a && v <= b);
|
|
376
|
+
expect(f.dependencies).toContain(min);
|
|
377
|
+
expect(f.dependencies).toContain(max);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('returns null dependencies when all sources are static', () => {
|
|
381
|
+
const f = createMultiSourceFilter([0, 10], (v, [a, b]) => v >= a && v <= b);
|
|
382
|
+
expect(f.dependencies).toBeNull();
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('callback receives resolved values', () => {
|
|
386
|
+
const min = Observable(5);
|
|
387
|
+
const f = createMultiSourceFilter([min, 10], (v, [a, b]) => v >= a && v <= b);
|
|
388
|
+
expect(f.callback(7)).toBe(true);
|
|
389
|
+
min.set(8);
|
|
390
|
+
expect(f.callback(7)).toBe(false);
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
});
|
package/types/elements.d.ts
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import { ObservableItem } from './observable';
|
|
3
3
|
import { BindingHydrator } from './template-cloner';
|
|
4
4
|
import { NDElement } from './nd-element';
|
|
5
|
+
import {AnchorAttributes,
|
|
6
|
+
AudioAttributes, CanvasAttributes, DetailsAttributes, DialogAttributes, GlobalAttributes, OlAttributes,
|
|
7
|
+
SourceAttributes, SvgAttributes, TrackAttributes, VideoAttributes, ThAttributes, ModAttributes, TdAttributes, TimeAttributes } from './globals';
|
|
5
8
|
|
|
6
9
|
// - Re-export all attribute types and utilities from globals
|
|
7
10
|
export type {
|
|
@@ -75,6 +78,8 @@ export type NdHTMLElement<T extends HTMLElement = HTMLElement> = T & { nd: NDEle
|
|
|
75
78
|
// Text & structural elements
|
|
76
79
|
// ---------------------------------------------------------
|
|
77
80
|
|
|
81
|
+
|
|
82
|
+
export declare function Text(string: string): string | Array<string | ObservableItem>;
|
|
78
83
|
export declare const Div: ElementFunction<GlobalAttributes, HTMLDivElement>;
|
|
79
84
|
export declare const Span: ElementFunction<GlobalAttributes, HTMLSpanElement>;
|
|
80
85
|
export declare const P: ElementFunction<GlobalAttributes, HTMLParagraphElement>;
|
package/vitest.config.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
test: {
|
|
5
|
+
environment: 'happy-dom',
|
|
6
|
+
globals: true,
|
|
7
|
+
include: ['tests/**/*.test.js'],
|
|
8
|
+
coverage: {
|
|
9
|
+
provider: 'v8',
|
|
10
|
+
reporter: ['text', 'html'],
|
|
11
|
+
include: ['src/**/*.js'],
|
|
12
|
+
exclude: ['src/**/*.d.ts', 'src/ui/**/*.css'],
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
});
|