@adia-ai/web-components 0.0.29 → 0.0.34
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/components/accordion/accordion.a2ui.json +1 -1
- package/components/accordion/accordion.js +6 -6
- package/components/accordion/accordion.yaml +1 -1
- package/components/action-list/action-list.a2ui.json +1 -1
- package/components/action-list/action-list.js +6 -6
- package/components/action-list/action-list.yaml +1 -1
- package/components/agent-artifact/agent-artifact.a2ui.json +1 -1
- package/components/agent-artifact/agent-artifact.js +4 -4
- package/components/agent-artifact/agent-artifact.yaml +1 -1
- package/components/agent-feedback-bar/agent-feedback-bar.a2ui.json +1 -1
- package/components/agent-feedback-bar/agent-feedback-bar.js +4 -4
- package/components/agent-feedback-bar/agent-feedback-bar.yaml +1 -1
- package/components/agent-questions/agent-questions.a2ui.json +1 -1
- package/components/agent-questions/agent-questions.js +4 -4
- package/components/agent-questions/agent-questions.yaml +1 -1
- package/components/agent-reasoning/agent-reasoning.a2ui.json +3 -3
- package/components/agent-reasoning/agent-reasoning.js +4 -4
- package/components/agent-reasoning/agent-reasoning.yaml +3 -3
- package/components/agent-suggestions/agent-suggestions.a2ui.json +1 -1
- package/components/agent-suggestions/agent-suggestions.js +4 -4
- package/components/agent-suggestions/agent-suggestions.yaml +1 -1
- package/components/agent-trace/agent-trace.a2ui.json +1 -1
- package/components/agent-trace/agent-trace.js +4 -4
- package/components/agent-trace/agent-trace.yaml +1 -1
- package/components/alert/alert.a2ui.json +1 -1
- package/components/alert/alert.js +4 -4
- package/components/alert/alert.yaml +1 -1
- package/components/aside/aside.a2ui.json +1 -1
- package/components/aside/aside.yaml +1 -1
- package/components/avatar/avatar.a2ui.json +1 -1
- package/components/avatar/avatar.js +8 -8
- package/components/avatar/avatar.yaml +1 -1
- package/components/badge/badge.a2ui.json +1 -1
- package/components/badge/badge.js +4 -4
- package/components/badge/badge.yaml +1 -1
- package/components/block/block.a2ui.json +1 -1
- package/components/block/block.js +4 -4
- package/components/block/block.yaml +1 -1
- package/components/breadcrumb/breadcrumb.a2ui.json +1 -1
- package/components/breadcrumb/breadcrumb.js +4 -4
- package/components/breadcrumb/breadcrumb.yaml +1 -1
- package/components/button/button.a2ui.json +1 -1
- package/components/button/button.js +4 -4
- package/components/button/button.yaml +1 -1
- package/components/calendar-picker/calendar-picker.a2ui.json +1 -1
- package/components/calendar-picker/calendar-picker.js +6 -6
- package/components/calendar-picker/calendar-picker.yaml +1 -1
- package/components/canvas/canvas.a2ui.json +1 -1
- package/components/canvas/canvas.js +4 -4
- package/components/canvas/canvas.yaml +1 -1
- package/components/card/card.a2ui.json +1 -1
- package/components/card/card.js +4 -4
- package/components/card/card.yaml +1 -1
- package/components/chart/chart.a2ui.json +1 -1
- package/components/chart/chart.js +5 -5
- package/components/chart/chart.yaml +1 -1
- package/components/chart-legend/chart-legend.a2ui.json +1 -1
- package/components/chart-legend/chart-legend.js +7 -7
- package/components/chart-legend/chart-legend.yaml +1 -1
- package/components/{chat → chat-thread}/chat-input.js +5 -5
- package/components/{chat/chat.a2ui.json → chat-thread/chat-thread.a2ui.json} +6 -6
- package/components/{chat/chat.css → chat-thread/chat-thread.css} +2 -2
- package/components/{chat/chat.js → chat-thread/chat-thread.js} +7 -7
- package/components/{chat/chat.yaml → chat-thread/chat-thread.yaml} +4 -4
- package/components/check/check.a2ui.json +1 -1
- package/components/check/check.js +5 -5
- package/components/check/check.yaml +1 -1
- package/components/code/code.a2ui.json +1 -1
- package/components/code/code.js +4 -4
- package/components/code/code.yaml +1 -1
- package/components/col/col.a2ui.json +1 -1
- package/components/col/col.js +4 -4
- package/components/col/col.yaml +1 -1
- package/components/color-picker/color-picker.a2ui.json +1 -1
- package/components/color-picker/color-picker.js +6 -6
- package/components/color-picker/color-picker.yaml +1 -1
- package/components/command/command.a2ui.json +1 -1
- package/components/command/command.js +5 -5
- package/components/command/command.yaml +1 -1
- package/components/description-list/description-list.a2ui.json +1 -1
- package/components/description-list/description-list.js +4 -4
- package/components/description-list/description-list.yaml +1 -1
- package/components/divider/divider.a2ui.json +1 -1
- package/components/divider/divider.js +4 -4
- package/components/divider/divider.yaml +1 -1
- package/components/drawer/drawer.a2ui.json +1 -1
- package/components/drawer/drawer.js +4 -4
- package/components/drawer/drawer.yaml +1 -1
- package/components/embed/embed.a2ui.json +1 -1
- package/components/embed/embed.js +4 -4
- package/components/embed/embed.yaml +1 -1
- package/components/empty-state/empty-state.a2ui.json +1 -1
- package/components/empty-state/empty-state.js +4 -4
- package/components/empty-state/empty-state.yaml +1 -1
- package/components/feed/feed-item.yaml +2 -2
- package/components/feed/feed.a2ui.json +2 -2
- package/components/feed/feed.css +12 -3
- package/components/feed/feed.js +22 -22
- package/components/feed/feed.yaml +2 -2
- package/components/field/field.a2ui.json +1 -1
- package/components/field/field.js +10 -10
- package/components/field/field.yaml +2 -2
- package/components/footer/footer.a2ui.json +1 -1
- package/components/footer/footer.yaml +1 -1
- package/components/grid/grid.a2ui.json +1 -1
- package/components/grid/grid.js +4 -4
- package/components/grid/grid.yaml +1 -1
- package/components/header/header.a2ui.json +1 -1
- package/components/header/header.yaml +1 -1
- package/components/heatmap/heatmap.a2ui.json +1 -1
- package/components/heatmap/heatmap.js +4 -4
- package/components/heatmap/heatmap.yaml +1 -1
- package/components/icon/icon.a2ui.json +1 -1
- package/components/icon/icon.js +4 -4
- package/components/icon/icon.yaml +1 -1
- package/components/image/image.a2ui.json +1 -1
- package/components/image/image.js +4 -4
- package/components/image/image.yaml +1 -1
- package/components/index.js +89 -85
- package/components/input/input.a2ui.json +1 -1
- package/components/input/input.js +7 -7
- package/components/input/input.yaml +1 -1
- package/components/inspector/inspector.a2ui.json +1 -1
- package/components/inspector/inspector.js +4 -4
- package/components/inspector/inspector.yaml +1 -1
- package/components/kbd/kbd.a2ui.json +1 -1
- package/components/kbd/kbd.js +4 -4
- package/components/kbd/kbd.yaml +1 -1
- package/components/list/list.a2ui.json +1 -1
- package/components/list/list.js +6 -6
- package/components/list/list.yaml +1 -1
- package/components/menu/menu.a2ui.json +1 -1
- package/components/menu/menu.js +8 -8
- package/components/menu/menu.yaml +1 -1
- package/components/modal/modal.a2ui.json +1 -1
- package/components/modal/modal.js +4 -4
- package/components/modal/modal.yaml +1 -1
- package/components/nav/nav.a2ui.json +98 -0
- package/components/nav/nav.css +133 -0
- package/components/nav/nav.js +140 -0
- package/components/nav/nav.test.js +428 -0
- package/components/nav/nav.yaml +114 -0
- package/components/nav-group/nav-group.a2ui.json +100 -0
- package/components/nav-group/nav-group.css +317 -0
- package/components/nav-group/nav-group.js +142 -0
- package/components/nav-group/nav-group.yaml +69 -0
- package/components/nav-item/nav-item.a2ui.json +106 -0
- package/components/nav-item/nav-item.css +194 -0
- package/components/nav-item/nav-item.js +76 -0
- package/components/nav-item/nav-item.yaml +73 -0
- package/components/noodles/noodles.a2ui.json +1 -1
- package/components/noodles/noodles.js +4 -4
- package/components/noodles/noodles.yaml +1 -1
- package/components/option-card/option-card.a2ui.json +1 -1
- package/components/option-card/option-card.js +6 -6
- package/components/option-card/option-card.yaml +1 -1
- package/components/otp-input/otp-input.a2ui.json +1 -1
- package/components/otp-input/otp-input.js +5 -5
- package/components/otp-input/otp-input.yaml +1 -1
- package/components/page/page.a2ui.json +3 -3
- package/components/page/page.js +4 -4
- package/components/page/page.yaml +3 -3
- package/components/pagination/pagination.a2ui.json +1 -1
- package/components/pagination/pagination.js +4 -4
- package/components/pagination/pagination.yaml +1 -1
- package/components/pane/pane.a2ui.json +1 -1
- package/components/pane/pane.js +4 -4
- package/components/pane/pane.yaml +1 -1
- package/components/pipeline-status/pipeline-status.a2ui.json +1 -1
- package/components/pipeline-status/pipeline-status.js +4 -4
- package/components/pipeline-status/pipeline-status.yaml +1 -1
- package/components/popover/popover.a2ui.json +1 -1
- package/components/popover/popover.js +4 -4
- package/components/popover/popover.yaml +1 -1
- package/components/progress/progress.a2ui.json +1 -1
- package/components/progress/progress.js +4 -4
- package/components/progress/progress.yaml +1 -1
- package/components/progress-row/progress-row.a2ui.json +1 -1
- package/components/progress-row/progress-row.js +4 -4
- package/components/progress-row/progress-row.yaml +1 -1
- package/components/radio/radio.a2ui.json +1 -1
- package/components/radio/radio.js +5 -5
- package/components/radio/radio.yaml +1 -1
- package/components/range/range.a2ui.json +1 -1
- package/components/range/range.js +7 -7
- package/components/range/range.yaml +1 -1
- package/components/rating/rating.a2ui.json +1 -1
- package/components/rating/rating.js +6 -6
- package/components/rating/rating.yaml +1 -1
- package/components/richtext/richtext.a2ui.json +1 -1
- package/components/richtext/richtext.js +4 -4
- package/components/richtext/richtext.yaml +1 -1
- package/components/row/row.a2ui.json +1 -1
- package/components/row/row.js +4 -4
- package/components/row/row.yaml +1 -1
- package/components/search/search.a2ui.json +1 -1
- package/components/search/search.js +5 -5
- package/components/search/search.yaml +1 -1
- package/components/section/section.a2ui.json +1 -1
- package/components/section/section.yaml +1 -1
- package/components/segment/segment.a2ui.json +1 -1
- package/components/segment/segment.js +4 -4
- package/components/segment/segment.yaml +1 -1
- package/components/segmented/segmented.a2ui.json +1 -1
- package/components/segmented/segmented.css +6 -0
- package/components/segmented/segmented.js +7 -7
- package/components/segmented/segmented.yaml +1 -1
- package/components/select/select.a2ui.json +1 -1
- package/components/select/select.js +5 -5
- package/components/select/select.yaml +1 -1
- package/components/skeleton/skeleton.a2ui.json +1 -1
- package/components/skeleton/skeleton.js +4 -4
- package/components/skeleton/skeleton.yaml +1 -1
- package/components/slider/slider.a2ui.json +1 -1
- package/components/slider/slider.js +7 -7
- package/components/slider/slider.yaml +1 -1
- package/components/stack/stack.a2ui.json +1 -1
- package/components/stack/stack.js +4 -4
- package/components/stack/stack.yaml +1 -1
- package/components/stat/stat.a2ui.json +1 -1
- package/components/stat/stat.js +4 -4
- package/components/stat/stat.yaml +1 -1
- package/components/step-progress/step-progress.a2ui.json +111 -0
- package/components/step-progress/step-progress.css +61 -0
- package/components/step-progress/step-progress.js +88 -0
- package/components/step-progress/step-progress.test.js +118 -0
- package/components/step-progress/step-progress.yaml +93 -0
- package/components/stepper/stepper.a2ui.json +1 -1
- package/components/stepper/stepper.js +6 -6
- package/components/stepper/stepper.yaml +1 -1
- package/components/stream/stream.a2ui.json +1 -1
- package/components/stream/stream.js +4 -4
- package/components/stream/stream.yaml +1 -1
- package/components/swatch/swatch.a2ui.json +1 -1
- package/components/swatch/swatch.js +4 -4
- package/components/swatch/swatch.yaml +1 -1
- package/components/swiper/swiper.a2ui.json +1 -1
- package/components/swiper/swiper.js +4 -4
- package/components/swiper/swiper.yaml +1 -1
- package/components/switch/switch.a2ui.json +1 -1
- package/components/switch/switch.js +5 -5
- package/components/switch/switch.yaml +1 -1
- package/components/table/table.a2ui.json +1 -1
- package/components/table/table.js +4 -4
- package/components/table/table.yaml +1 -1
- package/components/table-toolbar/table-toolbar.a2ui.json +1 -1
- package/components/table-toolbar/table-toolbar.js +4 -4
- package/components/table-toolbar/table-toolbar.yaml +1 -1
- package/components/tabs/tab.js +4 -4
- package/components/tabs/tabs.a2ui.json +1 -1
- package/components/tabs/tabs.js +5 -5
- package/components/tabs/tabs.yaml +1 -1
- package/components/tag/tag.a2ui.json +1 -1
- package/components/tag/tag.js +4 -4
- package/components/tag/tag.yaml +1 -1
- package/components/text/text.a2ui.json +1 -1
- package/components/text/text.js +4 -4
- package/components/text/text.yaml +1 -1
- package/components/textarea/textarea.a2ui.json +1 -1
- package/components/textarea/textarea.js +5 -5
- package/components/textarea/textarea.yaml +1 -1
- package/components/timeline/timeline.a2ui.json +1 -1
- package/components/timeline/timeline.js +6 -6
- package/components/timeline/timeline.yaml +1 -1
- package/components/toast/toast.a2ui.json +1 -1
- package/components/toast/toast.js +18 -18
- package/components/toast/toast.yaml +1 -1
- package/components/toggle-group/toggle-group.a2ui.json +1 -1
- package/components/toggle-group/toggle-group.js +6 -6
- package/components/toggle-group/toggle-group.yaml +1 -1
- package/components/toolbar/toolbar.a2ui.json +1 -1
- package/components/toolbar/toolbar.js +6 -6
- package/components/toolbar/toolbar.yaml +1 -1
- package/components/tooltip/tooltip.a2ui.json +1 -1
- package/components/tooltip/tooltip.js +7 -7
- package/components/tooltip/tooltip.yaml +1 -1
- package/components/tree/tree.a2ui.json +1 -1
- package/components/tree/tree.js +6 -6
- package/components/tree/tree.yaml +1 -1
- package/components/upload/upload.a2ui.json +1 -1
- package/components/upload/upload.js +6 -6
- package/components/upload/upload.yaml +1 -1
- package/core/element.js +4 -4
- package/core/element.test.js +18 -18
- package/core/form.js +9 -9
- package/core/index.js +2 -2
- package/core/provider.js +7 -7
- package/core/template.js +1 -1
- package/index.css +1 -1
- package/index.js +10 -8
- package/package.json +1 -1
- package/styles/components.css +11 -6
- package/styles/resets.css +1 -1
- package/traits/define.js +2 -2
- /package/components/{chat → chat-thread}/chat-input.css +0 -0
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
+
import '../../core/element.js';
|
|
3
|
+
import './nav.js';
|
|
4
|
+
import '../nav-group/nav-group.js';
|
|
5
|
+
import '../nav-item/nav-item.js';
|
|
6
|
+
|
|
7
|
+
const tick = () => new Promise((r) => queueMicrotask(r));
|
|
8
|
+
|
|
9
|
+
function mount(html) {
|
|
10
|
+
const wrap = document.createElement('div');
|
|
11
|
+
wrap.innerHTML = html;
|
|
12
|
+
document.body.appendChild(wrap);
|
|
13
|
+
return wrap.firstElementChild;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe('nav-ui', () => {
|
|
17
|
+
beforeEach(() => { document.body.innerHTML = ''; });
|
|
18
|
+
|
|
19
|
+
it('registers nav-ui, nav-group-ui, and nav-item-ui as custom elements', () => {
|
|
20
|
+
expect(customElements.get('nav-ui')).toBeDefined();
|
|
21
|
+
expect(customElements.get('nav-group-ui')).toBeDefined();
|
|
22
|
+
expect(customElements.get('nav-item-ui')).toBeDefined();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('defaults to variant="primary" with role=navigation', () => {
|
|
26
|
+
const nav = mount('<nav-ui></nav-ui>');
|
|
27
|
+
expect(nav.variant).toBe('primary');
|
|
28
|
+
expect(nav.getAttribute('role')).toBe('navigation');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('reflects [variant="section"] from the property', () => {
|
|
32
|
+
const nav = mount('<nav-ui variant="section"></nav-ui>');
|
|
33
|
+
expect(nav.variant).toBe('section');
|
|
34
|
+
expect(nav.getAttribute('variant')).toBe('section');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('uses [heading] as aria-label and removes it when cleared', async () => {
|
|
38
|
+
const nav = mount('<nav-ui heading="On this page"></nav-ui>');
|
|
39
|
+
expect(nav.getAttribute('aria-label')).toBe('On this page');
|
|
40
|
+
nav.heading = '';
|
|
41
|
+
await tick();
|
|
42
|
+
expect(nav.hasAttribute('aria-label')).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('section variant skips the ResizeObserver wiring', () => {
|
|
46
|
+
const primary = mount('<nav-ui></nav-ui>');
|
|
47
|
+
const section = mount('<nav-ui variant="section"></nav-ui>');
|
|
48
|
+
// The internal ResizeObserver is private (`#ro`); verify by the
|
|
49
|
+
// observable surface — toggle() is a no-op on section, but the
|
|
50
|
+
// primary nav can be flipped collapsed/uncollapsed.
|
|
51
|
+
section.toggle();
|
|
52
|
+
expect(section.collapsed).toBe(false);
|
|
53
|
+
primary.toggle();
|
|
54
|
+
expect(primary.collapsed).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('select(item) sets [selected], clears prior selection, and bubbles nav-select', () => {
|
|
58
|
+
const nav = mount(`
|
|
59
|
+
<nav-ui>
|
|
60
|
+
<nav-item-ui id="a" text="Alpha" value="/a"></nav-item-ui>
|
|
61
|
+
<nav-item-ui id="b" text="Bravo" value="/b" selected></nav-item-ui>
|
|
62
|
+
</nav-ui>
|
|
63
|
+
`);
|
|
64
|
+
const a = nav.querySelector('#a');
|
|
65
|
+
const b = nav.querySelector('#b');
|
|
66
|
+
|
|
67
|
+
const events = [];
|
|
68
|
+
nav.addEventListener('nav-select', (e) => events.push(e.detail));
|
|
69
|
+
|
|
70
|
+
nav.select(a);
|
|
71
|
+
|
|
72
|
+
expect(a.hasAttribute('selected')).toBe(true);
|
|
73
|
+
expect(b.hasAttribute('selected')).toBe(false);
|
|
74
|
+
expect(nav.selectedItem).toBe(a);
|
|
75
|
+
expect(events).toHaveLength(1);
|
|
76
|
+
expect(events[0]).toMatchObject({ item: a, text: 'Alpha', value: '/a' });
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('selectedItem getter returns null when no item is selected', () => {
|
|
80
|
+
const nav = mount(`
|
|
81
|
+
<nav-ui>
|
|
82
|
+
<nav-item-ui text="Alpha" value="/a"></nav-item-ui>
|
|
83
|
+
</nav-ui>
|
|
84
|
+
`);
|
|
85
|
+
expect(nav.selectedItem).toBeNull();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('toggle() is a no-op on variant="section"', () => {
|
|
89
|
+
const nav = mount('<nav-ui variant="section"></nav-ui>');
|
|
90
|
+
nav.toggle();
|
|
91
|
+
expect(nav.collapsed).toBe(false);
|
|
92
|
+
nav.toggle();
|
|
93
|
+
expect(nav.collapsed).toBe(false);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('survives disconnect without throwing', () => {
|
|
97
|
+
const nav = mount(`
|
|
98
|
+
<nav-ui>
|
|
99
|
+
<nav-item-ui text="Alpha"></nav-item-ui>
|
|
100
|
+
</nav-ui>
|
|
101
|
+
`);
|
|
102
|
+
expect(() => nav.remove()).not.toThrow();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe('nav-item-ui', () => {
|
|
107
|
+
beforeEach(() => { document.body.innerHTML = ''; });
|
|
108
|
+
|
|
109
|
+
it('sets role=link and tabindex=0 by default', () => {
|
|
110
|
+
const nav = mount(`
|
|
111
|
+
<nav-ui>
|
|
112
|
+
<nav-item-ui text="Alpha"></nav-item-ui>
|
|
113
|
+
</nav-ui>
|
|
114
|
+
`);
|
|
115
|
+
const item = nav.querySelector('nav-item-ui');
|
|
116
|
+
expect(item.getAttribute('role')).toBe('link');
|
|
117
|
+
expect(item.getAttribute('tabindex')).toBe('0');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('switches tabindex to -1 when [disabled]', async () => {
|
|
121
|
+
const nav = mount(`
|
|
122
|
+
<nav-ui>
|
|
123
|
+
<nav-item-ui text="Alpha"></nav-item-ui>
|
|
124
|
+
</nav-ui>
|
|
125
|
+
`);
|
|
126
|
+
const item = nav.querySelector('nav-item-ui');
|
|
127
|
+
item.disabled = true;
|
|
128
|
+
await tick();
|
|
129
|
+
expect(item.getAttribute('tabindex')).toBe('-1');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('click on the item calls parent nav-ui.select(this)', () => {
|
|
133
|
+
const nav = mount(`
|
|
134
|
+
<nav-ui>
|
|
135
|
+
<nav-item-ui id="a" text="Alpha" value="/a"></nav-item-ui>
|
|
136
|
+
</nav-ui>
|
|
137
|
+
`);
|
|
138
|
+
const item = nav.querySelector('#a');
|
|
139
|
+
const spy = vi.spyOn(nav, 'select');
|
|
140
|
+
|
|
141
|
+
item.click();
|
|
142
|
+
|
|
143
|
+
expect(spy).toHaveBeenCalledWith(item);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('Enter key activates a nav-item-ui (same as click)', () => {
|
|
147
|
+
const nav = mount(`
|
|
148
|
+
<nav-ui>
|
|
149
|
+
<nav-item-ui id="a" text="Alpha" value="/a"></nav-item-ui>
|
|
150
|
+
</nav-ui>
|
|
151
|
+
`);
|
|
152
|
+
const item = nav.querySelector('#a');
|
|
153
|
+
const spy = vi.spyOn(nav, 'select');
|
|
154
|
+
|
|
155
|
+
item.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
|
|
156
|
+
|
|
157
|
+
expect(spy).toHaveBeenCalledWith(item);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('Space key activates a nav-item-ui', () => {
|
|
161
|
+
const nav = mount(`
|
|
162
|
+
<nav-ui>
|
|
163
|
+
<nav-item-ui id="a" text="Alpha"></nav-item-ui>
|
|
164
|
+
</nav-ui>
|
|
165
|
+
`);
|
|
166
|
+
const item = nav.querySelector('#a');
|
|
167
|
+
const spy = vi.spyOn(nav, 'select');
|
|
168
|
+
|
|
169
|
+
item.dispatchEvent(new KeyboardEvent('keydown', { key: ' ', bubbles: true }));
|
|
170
|
+
|
|
171
|
+
expect(spy).toHaveBeenCalledWith(item);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('disabled nav-item-ui does not activate on click or keyboard', () => {
|
|
175
|
+
const nav = mount(`
|
|
176
|
+
<nav-ui>
|
|
177
|
+
<nav-item-ui id="a" text="Alpha" disabled></nav-item-ui>
|
|
178
|
+
</nav-ui>
|
|
179
|
+
`);
|
|
180
|
+
const item = nav.querySelector('#a');
|
|
181
|
+
const spy = vi.spyOn(nav, 'select');
|
|
182
|
+
|
|
183
|
+
item.click();
|
|
184
|
+
item.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
|
|
185
|
+
|
|
186
|
+
expect(spy).not.toHaveBeenCalled();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('selected item carries aria-current="page"', async () => {
|
|
190
|
+
const nav = mount(`
|
|
191
|
+
<nav-ui>
|
|
192
|
+
<nav-item-ui id="a" text="Alpha"></nav-item-ui>
|
|
193
|
+
</nav-ui>
|
|
194
|
+
`);
|
|
195
|
+
const item = nav.querySelector('#a');
|
|
196
|
+
nav.select(item);
|
|
197
|
+
await tick();
|
|
198
|
+
expect(item.getAttribute('aria-current')).toBe('page');
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('clearing selected removes aria-current', async () => {
|
|
202
|
+
const nav = mount(`
|
|
203
|
+
<nav-ui>
|
|
204
|
+
<nav-item-ui id="a" text="Alpha"></nav-item-ui>
|
|
205
|
+
<nav-item-ui id="b" text="Bravo"></nav-item-ui>
|
|
206
|
+
</nav-ui>
|
|
207
|
+
`);
|
|
208
|
+
const a = nav.querySelector('#a');
|
|
209
|
+
const b = nav.querySelector('#b');
|
|
210
|
+
nav.select(a);
|
|
211
|
+
await tick();
|
|
212
|
+
expect(a.getAttribute('aria-current')).toBe('page');
|
|
213
|
+
nav.select(b);
|
|
214
|
+
await tick();
|
|
215
|
+
expect(a.hasAttribute('aria-current')).toBe(false);
|
|
216
|
+
expect(b.getAttribute('aria-current')).toBe('page');
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe('nav-group-ui', () => {
|
|
221
|
+
beforeEach(() => { document.body.innerHTML = ''; });
|
|
222
|
+
|
|
223
|
+
it('auto-mints a header div with icon/text/caret slots', () => {
|
|
224
|
+
const nav = mount(`
|
|
225
|
+
<nav-ui>
|
|
226
|
+
<nav-group-ui text="Settings" icon="gear">
|
|
227
|
+
<nav-item-ui text="General"></nav-item-ui>
|
|
228
|
+
</nav-group-ui>
|
|
229
|
+
</nav-ui>
|
|
230
|
+
`);
|
|
231
|
+
const group = nav.querySelector('nav-group-ui');
|
|
232
|
+
const header = group.querySelector(':scope > [slot="header"]');
|
|
233
|
+
expect(header).not.toBeNull();
|
|
234
|
+
expect(header.querySelector('[slot="text"]').textContent).toBe('Settings');
|
|
235
|
+
expect(header.querySelector('icon-ui[slot="caret"]')).not.toBeNull();
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('renders [badge] in the header when set', () => {
|
|
239
|
+
const nav = mount(`
|
|
240
|
+
<nav-ui>
|
|
241
|
+
<nav-group-ui text="Settings" badge="3"></nav-group-ui>
|
|
242
|
+
</nav-ui>
|
|
243
|
+
`);
|
|
244
|
+
const group = nav.querySelector('nav-group-ui');
|
|
245
|
+
const badge = group.querySelector('[slot="header"] [slot="badge"]');
|
|
246
|
+
expect(badge).not.toBeNull();
|
|
247
|
+
expect(badge.textContent).toBe('3');
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('header click toggles [open] when not collapsed (delegated through nav-ui)', async () => {
|
|
251
|
+
const nav = mount(`
|
|
252
|
+
<nav-ui>
|
|
253
|
+
<nav-group-ui text="Settings"></nav-group-ui>
|
|
254
|
+
</nav-ui>
|
|
255
|
+
`);
|
|
256
|
+
// happy-dom returns 0 for getBoundingClientRect width, which makes the
|
|
257
|
+
// nav-ui treat itself as collapsed and route clicks to popover instead
|
|
258
|
+
// of inline expand. Stub the rect so the inline-expand path fires.
|
|
259
|
+
nav.getBoundingClientRect = () => ({ width: 240, height: 600, top: 0, left: 0, right: 240, bottom: 600, x: 0, y: 0 });
|
|
260
|
+
|
|
261
|
+
const group = nav.querySelector('nav-group-ui');
|
|
262
|
+
const header = group.querySelector(':scope > [slot="header"]');
|
|
263
|
+
expect(group.open).toBe(false);
|
|
264
|
+
|
|
265
|
+
header.click();
|
|
266
|
+
await tick();
|
|
267
|
+
|
|
268
|
+
expect(group.open).toBe(true);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('keydown Enter on header is ignored when collapsible=false', () => {
|
|
272
|
+
const nav = mount(`
|
|
273
|
+
<nav-ui>
|
|
274
|
+
<nav-group-ui text="Settings" open></nav-group-ui>
|
|
275
|
+
</nav-ui>
|
|
276
|
+
`);
|
|
277
|
+
const group = nav.querySelector('nav-group-ui');
|
|
278
|
+
group.collapsible = false;
|
|
279
|
+
|
|
280
|
+
const header = group.querySelector(':scope > [slot="header"]');
|
|
281
|
+
const events = [];
|
|
282
|
+
group.addEventListener('group-toggle', (e) => events.push(e.detail));
|
|
283
|
+
|
|
284
|
+
header.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
|
|
285
|
+
|
|
286
|
+
expect(events).toHaveLength(0);
|
|
287
|
+
expect(group.open).toBe(true);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('aria-expanded reflects the open state', async () => {
|
|
291
|
+
const nav = mount(`
|
|
292
|
+
<nav-ui>
|
|
293
|
+
<nav-group-ui text="Settings"></nav-group-ui>
|
|
294
|
+
</nav-ui>
|
|
295
|
+
`);
|
|
296
|
+
const group = nav.querySelector('nav-group-ui');
|
|
297
|
+
expect(group.getAttribute('aria-expanded')).toBe('false');
|
|
298
|
+
group.open = true;
|
|
299
|
+
await tick();
|
|
300
|
+
expect(group.getAttribute('aria-expanded')).toBe('true');
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('Enter on the header dispatches group-toggle when collapsible', () => {
|
|
304
|
+
const nav = mount(`
|
|
305
|
+
<nav-ui>
|
|
306
|
+
<nav-group-ui text="Settings"></nav-group-ui>
|
|
307
|
+
</nav-ui>
|
|
308
|
+
`);
|
|
309
|
+
const group = nav.querySelector('nav-group-ui');
|
|
310
|
+
const header = group.querySelector(':scope > [slot="header"]');
|
|
311
|
+
|
|
312
|
+
const events = [];
|
|
313
|
+
group.addEventListener('group-toggle', (e) => events.push(e.detail));
|
|
314
|
+
|
|
315
|
+
header.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
|
|
316
|
+
|
|
317
|
+
expect(events).toHaveLength(1);
|
|
318
|
+
expect(events[0]).toMatchObject({ text: 'Settings', open: true });
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it('respects an existing [slot="header"] instead of auto-minting one', () => {
|
|
322
|
+
const nav = mount(`
|
|
323
|
+
<nav-ui>
|
|
324
|
+
<nav-group-ui text="Settings">
|
|
325
|
+
<div slot="header" data-custom>my header</div>
|
|
326
|
+
</nav-group-ui>
|
|
327
|
+
</nav-ui>
|
|
328
|
+
`);
|
|
329
|
+
const group = nav.querySelector('nav-group-ui');
|
|
330
|
+
const headers = group.querySelectorAll(':scope > [slot="header"]');
|
|
331
|
+
expect(headers.length).toBe(1);
|
|
332
|
+
expect(headers[0].dataset.custom).toBe('');
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('survives disconnect without throwing', () => {
|
|
336
|
+
const nav = mount(`
|
|
337
|
+
<nav-ui>
|
|
338
|
+
<nav-group-ui text="Settings">
|
|
339
|
+
<nav-item-ui text="General"></nav-item-ui>
|
|
340
|
+
</nav-group-ui>
|
|
341
|
+
</nav-ui>
|
|
342
|
+
`);
|
|
343
|
+
expect(() => nav.remove()).not.toThrow();
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('section-variant subnav stamps groups + items per the docs site pattern', () => {
|
|
347
|
+
// Mirrors site/site.js subnav stamping for `subnav: true` sections:
|
|
348
|
+
// dividers in the sitemap become nav-group-ui kickers; following items
|
|
349
|
+
// become nav-item-ui children of the most recent group.
|
|
350
|
+
const nav = mount(`
|
|
351
|
+
<nav-ui variant="section" aria-label="Authentication">
|
|
352
|
+
<nav-group-ui text="Sign in">
|
|
353
|
+
<nav-item-ui text="Sign In" value="/auth/sign-in"></nav-item-ui>
|
|
354
|
+
<nav-item-ui text="Password" value="/auth/sign-in/password"></nav-item-ui>
|
|
355
|
+
</nav-group-ui>
|
|
356
|
+
<nav-group-ui text="Sign up">
|
|
357
|
+
<nav-item-ui text="Sign Up" value="/auth/sign-up"></nav-item-ui>
|
|
358
|
+
</nav-group-ui>
|
|
359
|
+
</nav-ui>
|
|
360
|
+
`);
|
|
361
|
+
const groups = nav.querySelectorAll(':scope > nav-group-ui');
|
|
362
|
+
expect(groups.length).toBe(2);
|
|
363
|
+
// Each group has its expected children attached (not stripped at mount).
|
|
364
|
+
expect(groups[0].querySelectorAll('nav-item-ui').length).toBe(2);
|
|
365
|
+
expect(groups[1].querySelectorAll('nav-item-ui').length).toBe(1);
|
|
366
|
+
// Group headers are auto-minted with the kicker text.
|
|
367
|
+
expect(groups[0].querySelector('[slot="header"] [slot="text"]').textContent).toBe('Sign in');
|
|
368
|
+
expect(groups[1].querySelector('[slot="header"] [slot="text"]').textContent).toBe('Sign up');
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
describe('section variant — explicit + cascade', () => {
|
|
373
|
+
beforeEach(() => { document.body.innerHTML = ''; });
|
|
374
|
+
|
|
375
|
+
it('nav-group-ui[variant="section"] reflects the property', () => {
|
|
376
|
+
const group = mount('<nav-group-ui variant="section" text="Settings"></nav-group-ui>');
|
|
377
|
+
expect(group.variant).toBe('section');
|
|
378
|
+
expect(group.getAttribute('variant')).toBe('section');
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it('nav-item-ui[variant="section"] reflects the property', () => {
|
|
382
|
+
const item = mount('<nav-item-ui variant="section" text="General"></nav-item-ui>');
|
|
383
|
+
expect(item.variant).toBe('section');
|
|
384
|
+
expect(item.getAttribute('variant')).toBe('section');
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('nav-group-ui defaults to variant="" (empty) when not set', () => {
|
|
388
|
+
const group = mount('<nav-group-ui text="Settings"></nav-group-ui>');
|
|
389
|
+
expect(group.variant).toBe('');
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('nav-item-ui defaults to variant="" (empty) when not set', () => {
|
|
393
|
+
const item = mount('<nav-item-ui text="General"></nav-item-ui>');
|
|
394
|
+
expect(item.variant).toBe('');
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('cascade: nav-ui[variant="section"] does NOT mutate the child variant prop', () => {
|
|
398
|
+
// The cascade is CSS-only — children keep their own (empty) variant.
|
|
399
|
+
// CSS rules target `nav-ui[variant="section"] > x:not([variant])` so
|
|
400
|
+
// the parent's variant only governs styling, not the JS state.
|
|
401
|
+
const nav = mount(`
|
|
402
|
+
<nav-ui variant="section">
|
|
403
|
+
<nav-group-ui text="Settings">
|
|
404
|
+
<nav-item-ui text="General"></nav-item-ui>
|
|
405
|
+
</nav-group-ui>
|
|
406
|
+
</nav-ui>
|
|
407
|
+
`);
|
|
408
|
+
const group = nav.querySelector('nav-group-ui');
|
|
409
|
+
const item = nav.querySelector('nav-item-ui');
|
|
410
|
+
expect(group.variant).toBe('');
|
|
411
|
+
expect(item.variant).toBe('');
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it('explicit variant on a child wins over the parent cascade', () => {
|
|
415
|
+
// A child can opt out of the section cascade by setting its own
|
|
416
|
+
// variant. The CSS rules use `:not([variant])` for the cascade path,
|
|
417
|
+
// so an explicit non-section variant on the child suppresses kicker
|
|
418
|
+
// styling and the child renders primary-style.
|
|
419
|
+
const nav = mount(`
|
|
420
|
+
<nav-ui variant="section">
|
|
421
|
+
<nav-group-ui text="Override" variant="primary"></nav-group-ui>
|
|
422
|
+
</nav-ui>
|
|
423
|
+
`);
|
|
424
|
+
const group = nav.querySelector('nav-group-ui');
|
|
425
|
+
expect(group.variant).toBe('primary');
|
|
426
|
+
expect(group.matches('[variant]')).toBe(true);
|
|
427
|
+
});
|
|
428
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
$schema: ../../../../scripts/schemas/component.yaml.schema.json
|
|
2
|
+
name: UINav
|
|
3
|
+
tag: nav-ui
|
|
4
|
+
component: Nav
|
|
5
|
+
category: layout
|
|
6
|
+
version: 1
|
|
7
|
+
description: |
|
|
8
|
+
Navigation rail. Consolidates the prior `app-nav-ui` + `section-nav-ui`
|
|
9
|
+
pair per ADR-0015 § Nav consolidation. [variant] drives visual
|
|
10
|
+
treatment; behavior is unified.
|
|
11
|
+
|
|
12
|
+
Default variant ("primary") is the app-sidebar nav: ResizeObserver
|
|
13
|
+
collapses to icon-only below 96px, groups open a popover when
|
|
14
|
+
collapsed. [variant="section"] is a subnav rail with quieter chrome
|
|
15
|
+
and a [heading] kicker rendered via CSS.
|
|
16
|
+
|
|
17
|
+
props:
|
|
18
|
+
variant:
|
|
19
|
+
type: string
|
|
20
|
+
default: primary
|
|
21
|
+
enum: [primary, section]
|
|
22
|
+
description: "Visual treatment. primary = app sidebar; section = subnav rail."
|
|
23
|
+
collapsed:
|
|
24
|
+
type: boolean
|
|
25
|
+
default: false
|
|
26
|
+
description: "Primary-variant only. Force icon-only collapse regardless of viewport width."
|
|
27
|
+
divider:
|
|
28
|
+
type: boolean
|
|
29
|
+
default: false
|
|
30
|
+
description: "Auto-place dividers between adjacent groups + items."
|
|
31
|
+
heading:
|
|
32
|
+
type: string
|
|
33
|
+
default: ''
|
|
34
|
+
description: "Optional kicker label. Section variant renders it via ::before; primary uses it as aria-label only."
|
|
35
|
+
|
|
36
|
+
events:
|
|
37
|
+
nav-select:
|
|
38
|
+
description: "Bubbles from <nav-item-ui> children when one is selected. Detail: { item, text, value }."
|
|
39
|
+
|
|
40
|
+
slots:
|
|
41
|
+
default:
|
|
42
|
+
description: "Primary slot — accepts <nav-group-ui> + <nav-item-ui> children, plus <hr data-nav-divider> for hand-placed dividers."
|
|
43
|
+
|
|
44
|
+
states:
|
|
45
|
+
- name: idle
|
|
46
|
+
description: Default, not collapsed.
|
|
47
|
+
- name: collapsed
|
|
48
|
+
description: Primary variant when [collapsed] or container width <= 96px.
|
|
49
|
+
|
|
50
|
+
traits: []
|
|
51
|
+
tokens: {}
|
|
52
|
+
a2ui:
|
|
53
|
+
rules: []
|
|
54
|
+
anti_patterns: []
|
|
55
|
+
|
|
56
|
+
examples:
|
|
57
|
+
- name: primary
|
|
58
|
+
description: App sidebar nav with groups + items.
|
|
59
|
+
a2ui: >-
|
|
60
|
+
[
|
|
61
|
+
{
|
|
62
|
+
"id": "root",
|
|
63
|
+
"component": "Nav",
|
|
64
|
+
"children": ["g1", "i1"]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"id": "g1",
|
|
68
|
+
"component": "NavGroup",
|
|
69
|
+
"text": "Settings",
|
|
70
|
+
"icon": "gear",
|
|
71
|
+
"children": ["g1i1"]
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"id": "g1i1",
|
|
75
|
+
"component": "NavItem",
|
|
76
|
+
"text": "General",
|
|
77
|
+
"value": "/settings/general"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"id": "i1",
|
|
81
|
+
"component": "NavItem",
|
|
82
|
+
"text": "Profile",
|
|
83
|
+
"icon": "user",
|
|
84
|
+
"value": "/profile"
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
- name: section
|
|
88
|
+
description: Subnav rail with heading.
|
|
89
|
+
a2ui: >-
|
|
90
|
+
[
|
|
91
|
+
{
|
|
92
|
+
"id": "root",
|
|
93
|
+
"component": "Nav",
|
|
94
|
+
"variant": "section",
|
|
95
|
+
"heading": "On this page",
|
|
96
|
+
"children": ["i1", "i2"]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"id": "i1",
|
|
100
|
+
"component": "NavItem",
|
|
101
|
+
"text": "Overview",
|
|
102
|
+
"value": "#overview"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"id": "i2",
|
|
106
|
+
"component": "NavItem",
|
|
107
|
+
"text": "API",
|
|
108
|
+
"value": "#api"
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
keywords: [nav, navigation, sidebar, menu, links]
|
|
113
|
+
synonyms: {}
|
|
114
|
+
related: []
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://adiaui.dev/a2ui/v0_9/components/NavGroup.json",
|
|
4
|
+
"title": "NavGroup",
|
|
5
|
+
"description": "Collapsible labeled group of <nav-item-ui> children inside <nav-ui>.\nConsolidates the prior `app-nav-group-ui` + `section-nav-group-ui`\nper ADR-0015 § Nav consolidation.\n\nWhen the parent <nav-ui> is collapsed (primary variant), clicking the\ngroup opens a popover with its children instead of toggling inline\nexpansion. Inline click + keyboard (Enter/Space) toggle is supported\nwhen [collapsible] (default true).\n",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"allOf": [
|
|
8
|
+
{
|
|
9
|
+
"$ref": "common_types.json#/$defs/ComponentCommon"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"$ref": "common_types.json#/$defs/CatalogComponentCommon"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"properties": {
|
|
16
|
+
"badge": {
|
|
17
|
+
"description": "Optional trailing badge (count, label).",
|
|
18
|
+
"type": "string",
|
|
19
|
+
"default": ""
|
|
20
|
+
},
|
|
21
|
+
"collapsible": {
|
|
22
|
+
"description": "When true, the header row toggles the open state on click/keyboard.",
|
|
23
|
+
"type": "boolean",
|
|
24
|
+
"default": true
|
|
25
|
+
},
|
|
26
|
+
"component": {
|
|
27
|
+
"const": "NavGroup"
|
|
28
|
+
},
|
|
29
|
+
"icon": {
|
|
30
|
+
"description": "Optional leading icon name (resolved via <icon-ui>).",
|
|
31
|
+
"type": "string",
|
|
32
|
+
"default": ""
|
|
33
|
+
},
|
|
34
|
+
"open": {
|
|
35
|
+
"description": "Inline-expanded state. Toggled by header click when [collapsible].",
|
|
36
|
+
"type": "boolean",
|
|
37
|
+
"default": false
|
|
38
|
+
},
|
|
39
|
+
"text": {
|
|
40
|
+
"description": "Visible group label.",
|
|
41
|
+
"type": "string",
|
|
42
|
+
"default": ""
|
|
43
|
+
},
|
|
44
|
+
"variant": {
|
|
45
|
+
"description": "Visual treatment. Default ('') renders as a primary-rail group (icon row, caret, collapsible). 'section' renders the header as a static kicker label with always-visible children — matches the prior <section-nav-group-ui>. When the parent <nav-ui> carries variant=\"section\", this group inherits it via CSS cascade unless an explicit variant is set on the group.",
|
|
46
|
+
"type": "string",
|
|
47
|
+
"enum": [
|
|
48
|
+
"",
|
|
49
|
+
"section"
|
|
50
|
+
],
|
|
51
|
+
"default": ""
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"required": [
|
|
55
|
+
"component"
|
|
56
|
+
],
|
|
57
|
+
"unevaluatedProperties": false,
|
|
58
|
+
"x-adiaui": {
|
|
59
|
+
"anti_patterns": [],
|
|
60
|
+
"category": "layout",
|
|
61
|
+
"events": {
|
|
62
|
+
"group-toggle": {
|
|
63
|
+
"description": "Fired when the header toggles via click/keyboard. Detail: { text, open }."
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"examples": [],
|
|
67
|
+
"keywords": [
|
|
68
|
+
"nav",
|
|
69
|
+
"navigation",
|
|
70
|
+
"group",
|
|
71
|
+
"sidebar",
|
|
72
|
+
"menu"
|
|
73
|
+
],
|
|
74
|
+
"name": "UINavGroup",
|
|
75
|
+
"related": [],
|
|
76
|
+
"slots": {
|
|
77
|
+
"default": {
|
|
78
|
+
"description": "Children — typically <nav-item-ui> rows."
|
|
79
|
+
},
|
|
80
|
+
"header": {
|
|
81
|
+
"description": "Optional custom header. Auto-generated when missing."
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"states": [
|
|
85
|
+
{
|
|
86
|
+
"description": "Default. Children hidden.",
|
|
87
|
+
"name": "closed"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"description": "Children visible inline.",
|
|
91
|
+
"name": "open"
|
|
92
|
+
}
|
|
93
|
+
],
|
|
94
|
+
"synonyms": {},
|
|
95
|
+
"tag": "nav-group-ui",
|
|
96
|
+
"tokens": {},
|
|
97
|
+
"traits": [],
|
|
98
|
+
"version": 1
|
|
99
|
+
}
|
|
100
|
+
}
|