@energysage/es-ds-components 6.3.1 → 6.3.3

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.
@@ -1,246 +0,0 @@
1
- // @vitest-environment happy-dom
2
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
- import type { App } from 'vue';
4
- import { createApp, nextTick, ref } from 'vue';
5
- import type { EsAutocompleteSuggestion } from '../types';
6
- import { useEsAutocompleteSearch } from './autocomplete-search';
7
-
8
- const DELAY = 300;
9
-
10
- const apps: App[] = [];
11
- beforeEach(() => {
12
- vi.useFakeTimers();
13
- });
14
- afterEach(() => {
15
- apps.forEach((app) => app.unmount());
16
- apps.length = 0;
17
- vi.useRealTimers();
18
- });
19
-
20
- /** run a composable inside a real component so lifecycle hooks work */
21
- function withSetup<T>(composable: () => T): { app: App; result: T } {
22
- let result!: T;
23
- const app = createApp({
24
- setup() {
25
- result = composable();
26
- return () => null;
27
- },
28
- });
29
- app.mount(document.createElement('div'));
30
- apps.push(app);
31
- return { app, result };
32
- }
33
-
34
- function makeSearch(overrides: { minChars?: number } = {}) {
35
- const model = ref('');
36
- const suggestions = ref<EsAutocompleteSuggestion[]>([]);
37
- const emitComplete = vi.fn();
38
- const emitSelect = vi.fn();
39
- const { app, result: search } = withSetup(() =>
40
- useEsAutocompleteSearch({
41
- delay: () => DELAY,
42
- emitComplete,
43
- emitSelect,
44
- minChars: () => overrides.minChars ?? 1,
45
- model,
46
- noResultsText: () => 'No results found',
47
- promptText: () => 'Type for suggestions',
48
- suggestions: () => suggestions.value,
49
- }),
50
- );
51
- /** type a value and let the model watcher run */
52
- async function type(value: string) {
53
- model.value = value;
54
- await nextTick();
55
- }
56
- return { app, emitComplete, emitSelect, model, search, suggestions, type };
57
- }
58
-
59
- describe('useEsAutocompleteSearch complete debouncing', () => {
60
- it('emits complete once per typing pause, with the trimmed query', async () => {
61
- const { emitComplete, type } = makeSearch();
62
- await type('s');
63
- vi.advanceTimersByTime(DELAY - 50);
64
- await type('so');
65
- await type(' solar ');
66
- vi.advanceTimersByTime(DELAY);
67
- expect(emitComplete).toHaveBeenCalledTimes(1);
68
- expect(emitComplete).toHaveBeenCalledWith('solar');
69
- });
70
-
71
- it('does not emit below minChars', async () => {
72
- const { emitComplete, type } = makeSearch({ minChars: 3 });
73
- await type('so');
74
- vi.advanceTimersByTime(DELAY * 2);
75
- expect(emitComplete).not.toHaveBeenCalled();
76
- });
77
-
78
- it('does not re-fetch when selection copies the suggestion text into the input', async () => {
79
- const { emitComplete, search, type } = makeSearch();
80
- // selection is terminal: Reka sets the model to the chosen text afterward
81
- search.onSelect({ id: 'a', text: 'solar batteries' });
82
- await type('solar batteries');
83
- vi.advanceTimersByTime(DELAY * 2);
84
- expect(emitComplete).not.toHaveBeenCalled();
85
-
86
- // ...but the suppression is one-shot: typing again fetches normally
87
- await type('solar batteries plus');
88
- vi.advanceTimersByTime(DELAY);
89
- expect(emitComplete).toHaveBeenCalledWith('solar batteries plus');
90
- });
91
-
92
- it('unmount cancels a pending complete', async () => {
93
- const { app, emitComplete, type } = makeSearch();
94
- await type('solar');
95
- app.unmount();
96
- vi.advanceTimersByTime(DELAY * 2);
97
- expect(emitComplete).not.toHaveBeenCalled();
98
- });
99
- });
100
-
101
- describe('useEsAutocompleteSearch panel message and suggestion gating', () => {
102
- it('walks prompt → results → no-results → prompt-while-pending correctly', async () => {
103
- const { search, suggestions, type } = makeSearch();
104
- // nothing searched yet
105
- expect(search.panelMessage.value).toBe('Type for suggestions');
106
-
107
- // results arrive: no message
108
- await type('solar');
109
- suggestions.value = [{ id: 'a', text: 'solar batteries' }];
110
- await nextTick();
111
- expect(search.panelMessage.value).toBe('');
112
-
113
- // a search came back empty: no-results
114
- await type('solarx');
115
- suggestions.value = [];
116
- await nextTick();
117
- expect(search.panelMessage.value).toBe('No results found');
118
-
119
- // the query changed again, so a new search is pending: prompt, not the
120
- // previous query's stale no-results
121
- await type('solarxy');
122
- expect(search.panelMessage.value).toBe('Type for suggestions');
123
- });
124
-
125
- it('exposes the no-results announcement exactly when a search came back empty', async () => {
126
- const { search, suggestions, type } = makeSearch();
127
- // nothing searched yet: nothing to announce
128
- expect(search.noResultsAnnouncement.value).toBe('');
129
-
130
- await type('solar');
131
- suggestions.value = [{ id: 'a', text: 'solar batteries' }];
132
- await nextTick();
133
- expect(search.noResultsAnnouncement.value).toBe('');
134
-
135
- // a search that came back empty announces the no-results message
136
- await type('solarx');
137
- suggestions.value = [];
138
- await nextTick();
139
- expect(search.noResultsAnnouncement.value).toBe('No results found');
140
-
141
- // a pending search announces nothing: the prompt is guidance, not a
142
- // state change
143
- await type('solarxy');
144
- expect(search.noResultsAnnouncement.value).toBe('');
145
- });
146
-
147
- it('holds back the stale list after a selection until the app answers again', async () => {
148
- const { search, suggestions, type } = makeSearch();
149
- await type('12');
150
- suggestions.value = [
151
- { id: 'a', text: '12 Maple Ave, Cambridge, MA 02138' },
152
- { id: 'b', text: '123 Main St, Boston, MA 02108' },
153
- ];
154
- await nextTick();
155
- expect(search.effectiveSuggestions.value).toHaveLength(2);
156
-
157
- // selection fills the input with the full text; the app's list matched
158
- // "12", not that text, so a refocused panel shows the prompt instead
159
- search.onSelect({ id: 'b', text: '123 Main St, Boston, MA 02108' });
160
- await type('123 Main St, Boston, MA 02108');
161
- expect(search.effectiveSuggestions.value).toHaveLength(0);
162
- expect(search.panelMessage.value).toBe('Type for suggestions');
163
-
164
- // editing fetches again, and the app's answer clears the staleness
165
- await type('123 Main St B');
166
- vi.advanceTimersByTime(DELAY);
167
- suggestions.value = [{ id: 'b', text: '123 Main St, Boston, MA 02108' }];
168
- await nextTick();
169
- expect(search.effectiveSuggestions.value).toHaveLength(1);
170
- expect(search.panelMessage.value).toBe('');
171
- });
172
-
173
- it('fills again after a selection even when the app answers with the same array', async () => {
174
- // an app that memoises per query hands back the identical array, so the
175
- // suggestions watcher never fires and only the edit can end the staleness
176
- const CACHED = [{ id: 'a', text: 'apple' }];
177
- const model = ref('');
178
- const search = withSetup(() =>
179
- useEsAutocompleteSearch({
180
- delay: () => DELAY,
181
- emitComplete: () => undefined,
182
- emitSelect: () => undefined,
183
- minChars: () => 1,
184
- model,
185
- noResultsText: () => 'No results found',
186
- promptText: () => 'Type for suggestions',
187
- suggestions: () => CACHED,
188
- }),
189
- ).result;
190
-
191
- model.value = 'app';
192
- await nextTick();
193
- expect(search.effectiveSuggestions.value).toHaveLength(1);
194
-
195
- search.onSelect(CACHED[0]!);
196
- model.value = 'apple';
197
- await nextTick();
198
- expect(search.effectiveSuggestions.value).toHaveLength(0);
199
-
200
- // searching again returns the very same array the app cached
201
- model.value = 'app';
202
- await nextTick();
203
- expect(search.effectiveSuggestions.value).toHaveLength(1);
204
- });
205
-
206
- it('shows suggestions an app refreshes in its own select handler', async () => {
207
- const { search, suggestions, type } = makeSearch();
208
- await type('12');
209
- suggestions.value = [{ id: 'a', text: '12 Maple Ave, Cambridge, MA 02138' }];
210
- await nextTick();
211
-
212
- // the app's select handler runs inside the select emit, before the
213
- // model picks up the chosen text
214
- search.onSelect({ id: 'a', text: '12 Maple Ave, Cambridge, MA 02138' });
215
- suggestions.value = [{ id: 'b', text: '12 Maple Ave rear unit, Cambridge, MA 02138' }];
216
- await type('12 Maple Ave, Cambridge, MA 02138');
217
- expect(search.effectiveSuggestions.value).toHaveLength(1);
218
- });
219
-
220
- it('shows the prompt, not no-results, when the app clears its list on select', async () => {
221
- const { search, suggestions, type } = makeSearch();
222
- await type('12');
223
- suggestions.value = [{ id: 'a', text: '12 Maple Ave, Cambridge, MA 02138' }];
224
- await nextTick();
225
-
226
- search.onSelect({ id: 'a', text: '12 Maple Ave, Cambridge, MA 02138' });
227
- suggestions.value = [];
228
- await type('12 Maple Ave, Cambridge, MA 02138');
229
- expect(search.effectiveSuggestions.value).toHaveLength(0);
230
- expect(search.panelMessage.value).toBe('Type for suggestions');
231
- });
232
-
233
- it('gates suggestions below minChars with a referentially stable empty list', async () => {
234
- const { search, suggestions, type } = makeSearch({ minChars: 3 });
235
- suggestions.value = [{ id: 'a', text: 'solar batteries' }];
236
- await type('so');
237
- const first = search.effectiveSuggestions.value;
238
- expect(first).toHaveLength(0);
239
- await type('s');
240
- // same reference: downstream watchers must not re-fire per keystroke
241
- expect(search.effectiveSuggestions.value).toBe(first);
242
-
243
- await type('sol');
244
- expect(search.effectiveSuggestions.value).toHaveLength(1);
245
- });
246
- });
@@ -1,209 +0,0 @@
1
- // @vitest-environment happy-dom
2
- import { afterEach, describe, expect, it } from 'vitest';
3
- import type { App } from 'vue';
4
- import { createApp, nextTick, ref } from 'vue';
5
- import type { EsAutocompleteSuggestion } from '../types';
6
- import { useEsAutocompleteVisibleRows } from './autocomplete-visible-rows';
7
-
8
- // NOTE: happy-dom has no real layout, so element geometry (offsetHeight,
9
- // clientHeight) is mocked per element. These tests cover the counting/limit
10
- // LOGIC — whole rows per available height, the min-1 floor, and which height
11
- // constraint wins. Whether real browser layout produces that geometry is
12
- // covered by the planned Playwright specs (plan §8a).
13
-
14
- const apps: App[] = [];
15
- afterEach(() => {
16
- apps.forEach((app) => app.unmount());
17
- apps.length = 0;
18
- document.body.innerHTML = '';
19
- });
20
-
21
- /** run a composable inside a real component so lifecycle hooks work */
22
- function withSetup<T>(composable: () => T): T {
23
- let result!: T;
24
- const app = createApp({
25
- setup() {
26
- result = composable();
27
- return () => null;
28
- },
29
- });
30
- app.mount(document.createElement('div'));
31
- apps.push(app);
32
- return result;
33
- }
34
-
35
- function suggestionList(count: number): EsAutocompleteSuggestion[] {
36
- return Array.from({ length: count }, (_, index) => ({ id: `s${index}`, text: `suggestion ${index}` }));
37
- }
38
-
39
- function makeContainer(
40
- rows: { count: number; height: number },
41
- size: { clientHeight?: number; maxHeight?: number },
42
- extras: { messageOnly?: boolean } = {},
43
- ) {
44
- const container = document.createElement('div');
45
- // happy-dom only resolves computed styles (the max-height limit) for
46
- // elements attached to the document
47
- document.body.append(container);
48
- if (size.maxHeight !== undefined) {
49
- container.style.maxHeight = `${size.maxHeight}px`;
50
- }
51
- Object.defineProperty(container, 'clientHeight', {
52
- configurable: true,
53
- get: () => size.clientHeight ?? 0,
54
- });
55
- if (extras.messageOnly) {
56
- container.append(document.createElement('div'));
57
- return container;
58
- }
59
- // rows share one uniform height, matching the es-autocomplete-item styles
60
- for (let index = 0; index < rows.count; index += 1) {
61
- const el = document.createElement('div');
62
- el.dataset.esAutocompleteItem = '';
63
- Object.defineProperty(el, 'offsetHeight', { configurable: true, value: rows.height });
64
- container.append(el);
65
- }
66
- return container;
67
- }
68
-
69
- describe('useEsAutocompleteVisibleRows', () => {
70
- it('renders the number of whole rows that fit the height limit', async () => {
71
- const contentEl = ref<HTMLElement | null>(makeContainer({ count: 7, height: 30 }, { maxHeight: 100 }));
72
- const suggestions = ref(suggestionList(7));
73
- const fit = withSetup(() => useEsAutocompleteVisibleRows(contentEl, suggestions, 10));
74
- await fit.remeasure();
75
- // floor(100 / 30) = 3 whole rows; the fourth would be partially shown
76
- expect(fit.visibleSuggestions.value).toHaveLength(3);
77
- expect(fit.measured.value).toBe(true);
78
- });
79
-
80
- it('never trims below one row, even when none fully fits', async () => {
81
- const contentEl = ref<HTMLElement | null>(makeContainer({ count: 1, height: 200 }, { maxHeight: 100 }));
82
- const suggestions = ref(suggestionList(5));
83
- const fit = withSetup(() => useEsAutocompleteVisibleRows(contentEl, suggestions, 10));
84
- await fit.remeasure();
85
- expect(fit.visibleSuggestions.value).toHaveLength(1);
86
- });
87
-
88
- it('a message-only panel (prompt/no-results, no rows) is measured and untrimmed', async () => {
89
- const contentEl = ref<HTMLElement | null>(
90
- makeContainer({ count: 0, height: 0 }, { maxHeight: 100 }, { messageOnly: true }),
91
- );
92
- const suggestions = ref<EsAutocompleteSuggestion[]>([]);
93
- const fit = withSetup(() => useEsAutocompleteVisibleRows(contentEl, suggestions, 10));
94
- await fit.remeasure();
95
- expect(fit.measured.value).toBe(true);
96
- });
97
-
98
- it('prefers the resolved max-height over the content-sized clientHeight', async () => {
99
- // clientHeight is content-limited (40) but max-height allows 100: the count
100
- // must come from what COULD fit, or a grown viewport never refills
101
- const contentEl = ref<HTMLElement | null>(
102
- makeContainer({ count: 5, height: 30 }, { clientHeight: 40, maxHeight: 100 }),
103
- );
104
- const suggestions = ref(suggestionList(5));
105
- const fit = withSetup(() => useEsAutocompleteVisibleRows(contentEl, suggestions, 10));
106
- await fit.remeasure();
107
- expect(fit.visibleSuggestions.value).toHaveLength(3);
108
- });
109
-
110
- it('falls back to clientHeight when there is no max-height (the mobile takeover list)', async () => {
111
- const contentEl = ref<HTMLElement | null>(makeContainer({ count: 5, height: 30 }, { clientHeight: 70 }));
112
- const suggestions = ref(suggestionList(5));
113
- const fit = withSetup(() => useEsAutocompleteVisibleRows(contentEl, suggestions, 10));
114
- await fit.remeasure();
115
- expect(fit.visibleSuggestions.value).toHaveLength(2);
116
- });
117
-
118
- it('re-measures when the suggestions change', async () => {
119
- const container = makeContainer({ count: 7, height: 30 }, { maxHeight: 100 });
120
- const contentEl = ref<HTMLElement | null>(container);
121
- const suggestions = ref(suggestionList(7));
122
- const fit = withSetup(() => useEsAutocompleteVisibleRows(contentEl, suggestions, 10));
123
- await fit.remeasure();
124
- expect(fit.visibleSuggestions.value).toHaveLength(3);
125
-
126
- container.style.maxHeight = '70px';
127
- suggestions.value = suggestionList(7);
128
- await nextTick(); // the watcher fires
129
- await nextTick(); // remeasure's internal tick
130
- expect(fit.visibleSuggestions.value).toHaveLength(2);
131
- });
132
-
133
- it('adds and removes whole rows on scroll as the available height changes', async () => {
134
- const container = makeContainer({ count: 2, height: 30 }, { maxHeight: 70 });
135
- const contentEl = ref<HTMLElement | null>(container);
136
- const suggestions = ref(suggestionList(7));
137
- const fit = withSetup(() => useEsAutocompleteVisibleRows(contentEl, suggestions, 10));
138
- await fit.remeasure();
139
- expect(fit.visibleSuggestions.value).toHaveLength(2);
140
-
141
- // scrolling moved the anchor and the popper granted more height: the
142
- // count grows from the row height alone, without extra rows pre-rendered
143
- container.style.maxHeight = '130px';
144
- window.dispatchEvent(new Event('scroll'));
145
- await new Promise((resolve) => requestAnimationFrame(resolve)); // the handler's deferred frame
146
- await nextTick(); // remeasure's internal tick
147
- await nextTick();
148
- expect(fit.visibleSuggestions.value).toHaveLength(4);
149
- });
150
-
151
- it('ignores viewport events while inactive: a closed, still-mounted panel never re-measures on scroll', async () => {
152
- const container = makeContainer({ count: 2, height: 30 }, { maxHeight: 70 });
153
- const contentEl = ref<HTMLElement | null>(container);
154
- const suggestions = ref(suggestionList(7));
155
- const active = ref(true);
156
- const fit = withSetup(() =>
157
- useEsAutocompleteVisibleRows(contentEl, suggestions, 10, { active: () => active.value }),
158
- );
159
- await fit.remeasure();
160
- expect(fit.visibleSuggestions.value).toHaveLength(2);
161
-
162
- active.value = false;
163
- container.style.maxHeight = '130px';
164
- window.dispatchEvent(new Event('scroll'));
165
- await new Promise((resolve) => requestAnimationFrame(resolve));
166
- await nextTick();
167
- await nextTick();
168
- expect(fit.visibleSuggestions.value).toHaveLength(2);
169
-
170
- // reactivating restores the viewport tracking
171
- active.value = true;
172
- window.dispatchEvent(new Event('scroll'));
173
- await new Promise((resolve) => requestAnimationFrame(resolve));
174
- await nextTick();
175
- await nextTick();
176
- expect(fit.visibleSuggestions.value).toHaveLength(4);
177
- });
178
-
179
- it('beforeMeasure runs in the same pass, so a limit it writes drives that count', async () => {
180
- const container = makeContainer({ count: 7, height: 30 }, {});
181
- const contentEl = ref<HTMLElement | null>(container);
182
- const suggestions = ref(suggestionList(7));
183
- // the caller positions the container and writes its max-height (the
184
- // desktop shell's positionPanel) right before each measurement
185
- const fit = withSetup(() =>
186
- useEsAutocompleteVisibleRows(contentEl, suggestions, 10, {
187
- beforeMeasure: () => {
188
- container.style.maxHeight = '70px';
189
- },
190
- }),
191
- );
192
- await fit.remeasure();
193
- expect(fit.visibleSuggestions.value).toHaveLength(2);
194
- });
195
-
196
- it('is unmeasured (list hidden) until a container exists', async () => {
197
- const contentEl = ref<HTMLElement | null>(null);
198
- const suggestions = ref(suggestionList(3));
199
- const fit = withSetup(() => useEsAutocompleteVisibleRows(contentEl, suggestions, 10));
200
- expect(fit.measured.value).toBe(false);
201
-
202
- contentEl.value = makeContainer({ count: 3, height: 30 }, { maxHeight: 100 });
203
- await nextTick(); // the watcher fires
204
- await new Promise((resolve) => requestAnimationFrame(resolve)); // the measure's deferred frame
205
- await nextTick(); // remeasure's internal tick
206
- expect(fit.measured.value).toBe(true);
207
- expect(fit.visibleSuggestions.value).toHaveLength(3);
208
- });
209
- });
@@ -1,103 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import type { EsAutocompleteTextSegment } from '../types';
3
- import { splitEsAutocompleteText, splitEsAutocompleteTextLines } from './autocomplete';
4
-
5
- // matched (typed) portions render regular; predictive portions render bold
6
- const regular = (text: string): EsAutocompleteTextSegment => ({ predictive: false, text });
7
- const bold = (text: string): EsAutocompleteTextSegment => ({ predictive: true, text });
8
-
9
- /** segments must always reconstruct the input exactly — no lost or duplicated characters */
10
- function expectReconstructs(text: string, segments: EsAutocompleteTextSegment[]) {
11
- expect(segments.map((segment) => segment.text).join('')).toBe(text);
12
- }
13
-
14
- describe('splitEsAutocompleteText', () => {
15
- it('bolds the predictive remainder of a prefix match', () => {
16
- expect(splitEsAutocompleteText('solar batteries', 'solar')).toEqual([regular('solar'), bold(' batteries')]);
17
- });
18
-
19
- it('matches case-insensitively', () => {
20
- expect(splitEsAutocompleteText('Solar Batteries', 'SOLAR')).toEqual([regular('Solar'), bold(' Batteries')]);
21
- });
22
-
23
- it('matches tokens in any order, at every word start', () => {
24
- expect(splitEsAutocompleteText('123 Main St, Boston, MA', 'boston main')).toEqual([
25
- bold('123 '),
26
- regular('Main'),
27
- bold(' St, '),
28
- regular('Boston'),
29
- bold(', MA'),
30
- ]);
31
- });
32
-
33
- it('prefers word starts: "st" matches "St" but not the middle of "Boston"', () => {
34
- expect(splitEsAutocompleteText('Boston St', 'st')).toEqual([bold('Boston '), regular('St')]);
35
- });
36
-
37
- it('falls back to mid-word matching when a token never matches at a word start', () => {
38
- expect(splitEsAutocompleteText('123 Main St', '3')).toEqual([bold('12'), regular('3'), bold(' Main St')]);
39
- });
40
-
41
- it('renders everything regular when nothing matches, and for a blank query', () => {
42
- expect(splitEsAutocompleteText('solar panels', 'xyz')).toEqual([regular('solar panels')]);
43
- expect(splitEsAutocompleteText('solar panels', ' ')).toEqual([regular('solar panels')]);
44
- });
45
-
46
- it('merges overlapping token matches', () => {
47
- expect(splitEsAutocompleteText('solar panels', 'solar sol')).toEqual([regular('solar'), bold(' panels')]);
48
- });
49
-
50
- it('never mis-slices when lowercasing changes string length (e.g. İ)', () => {
51
- const text = 'İzmir solar';
52
- const segments = splitEsAutocompleteText(text, 'solar');
53
- expectReconstructs(text, segments);
54
- expect(segments).toContainEqual(regular('solar'));
55
- });
56
-
57
- it('always reconstructs the input text exactly', () => {
58
- const cases: Array<[string, string]> = [
59
- ['solar batteries', 'solar'],
60
- ['123 Main St', 'main 123 st'],
61
- ['heat pump water heater', 'heat'],
62
- ['no match here', 'zzz'],
63
- ['', 'anything'],
64
- ];
65
- for (const [text, query] of cases) {
66
- expectReconstructs(text, splitEsAutocompleteText(text, query));
67
- }
68
- });
69
- });
70
-
71
- describe('splitEsAutocompleteTextLines', () => {
72
- const addressLines = ['1200 Beacon St', 'Brookline, MA 02446'];
73
-
74
- it('highlights tokens on the lines they match', () => {
75
- expect(splitEsAutocompleteTextLines(['123 Main St', 'Boston, MA 02108'], 'boston main')).toEqual([
76
- [bold('123 '), regular('Main'), bold(' St')],
77
- [regular('Boston'), bold(', MA 02108')],
78
- ]);
79
- });
80
-
81
- it('bolds a whole line without its own match when another line matched (bolding mode)', () => {
82
- expect(splitEsAutocompleteTextLines(addressLines, 'beacon')).toEqual([
83
- [bold('1200 '), regular('Beacon'), bold(' St')],
84
- [bold('Brookline, MA 02446')],
85
- ]);
86
- });
87
-
88
- it('renders every line regular when no line matches', () => {
89
- expect(splitEsAutocompleteTextLines(addressLines, 'zzz')).toEqual([
90
- [regular('1200 Beacon St')],
91
- [regular('Brookline, MA 02446')],
92
- ]);
93
- });
94
-
95
- it('decides the word-start fallback across all lines together', () => {
96
- // "st" has a word-start match on line 1, so it must NOT fall back to
97
- // mid-word matching inside "Brookline" on line 2
98
- expect(splitEsAutocompleteTextLines(addressLines, 'beacon st')).toEqual([
99
- [bold('1200 '), regular('Beacon'), bold(' '), regular('St')],
100
- [bold('Brookline, MA 02446')],
101
- ]);
102
- });
103
- });
package/eslint.config.mjs DELETED
@@ -1,37 +0,0 @@
1
- import withNuxt from '../es-ds-docs/.nuxt/eslint.config.mjs';
2
-
3
- export default withNuxt()
4
- .override('nuxt/vue/rules', {
5
- rules: {
6
- 'vue/component-name-in-template-casing': [
7
- 'error',
8
- 'kebab-case',
9
- {
10
- registeredComponentsOnly: false,
11
- ignores: [],
12
- },
13
- ],
14
- 'vue/block-order': [
15
- 'error',
16
- {
17
- order: [['script[setup]', 'script:not([setup])'], 'template', 'style'],
18
- },
19
- ],
20
- 'vue/block-lang': [
21
- 'error',
22
- {
23
- script: {
24
- lang: 'ts',
25
- },
26
- },
27
- ],
28
- 'vue/html-self-closing': 'off',
29
- },
30
- })
31
- .override('nuxt/typescript/rules', {
32
- rules: {
33
- '@typescript-eslint/no-explicit-any': 'off',
34
- '@typescript-eslint/no-unsafe-function-type': 'off',
35
- '@typescript-eslint/ban-ts-comment': 'off',
36
- },
37
- });
package/tsconfig.json DELETED
@@ -1,4 +0,0 @@
1
- {
2
- // https://nuxt.com/docs/guide/concepts/typescript
3
- "extends": "../es-ds-docs/.nuxt/tsconfig.json"
4
- }