svelte-ag 1.2.2 → 1.2.4
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/dist/components/search/combinations/SearchPassthroughHarness.svelte +41 -0
- package/dist/components/search/combinations/SearchPassthroughHarness.svelte.d.ts +19 -0
- package/dist/components/search/combinations/SearchPassthroughHarness.svelte.d.ts.map +1 -0
- package/dist/components/search/combinations/searchPassthrough.comp.test.d.ts +2 -0
- package/dist/components/search/combinations/searchPassthrough.comp.test.d.ts.map +1 -0
- package/dist/components/search/combinations/searchPassthrough.comp.test.js +65 -0
- package/dist/components/search/combinations/searchPopover.comp.test.d.ts +2 -0
- package/dist/components/search/combinations/searchPopover.comp.test.d.ts.map +1 -0
- package/dist/components/search/combinations/searchPopover.comp.test.js +36 -0
- package/dist/components/search/combinations/searchPopover.svelte +2 -2
- package/dist/components/search/components/search-list.svelte +11 -4
- package/dist/components/search/components/search-list.svelte.d.ts.map +1 -1
- package/dist/components/search/components/search-pagnation.svelte +9 -1
- package/dist/components/search/components/search-pagnation.svelte.d.ts.map +1 -1
- package/dist/vite/vite-plugin-component-source-collector.d.ts.map +1 -1
- package/dist/vite/vite-plugin-component-source-collector.js +2 -0
- package/package.json +3 -1
- package/src/lib/components/search/combinations/SearchPassthroughHarness.svelte +41 -0
- package/src/lib/components/search/combinations/searchPassthrough.comp.test.ts +79 -0
- package/src/lib/components/search/combinations/searchPopover.comp.test.ts +44 -0
- package/src/lib/components/search/combinations/searchPopover.svelte +2 -2
- package/src/lib/components/search/components/search-list.svelte +11 -4
- package/src/lib/components/search/components/search-pagnation.svelte +9 -1
- package/src/lib/vite/vite-plugin-component-source-collector.ts +3 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Search } from '../index.js';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
items,
|
|
6
|
+
value = $bindable(),
|
|
7
|
+
rootClass,
|
|
8
|
+
rootTestId,
|
|
9
|
+
inputClass,
|
|
10
|
+
inputLabel,
|
|
11
|
+
inputTestId,
|
|
12
|
+
listClass,
|
|
13
|
+
listTestId,
|
|
14
|
+
paginationClass,
|
|
15
|
+
paginationTestId,
|
|
16
|
+
perPage = $bindable(1)
|
|
17
|
+
}: {
|
|
18
|
+
items: Search.RootProps['items'];
|
|
19
|
+
value: Search.RootProps['value'];
|
|
20
|
+
rootClass?: string;
|
|
21
|
+
rootTestId?: string;
|
|
22
|
+
inputClass?: string;
|
|
23
|
+
inputLabel?: string;
|
|
24
|
+
inputTestId?: string;
|
|
25
|
+
listClass?: string;
|
|
26
|
+
listTestId?: string;
|
|
27
|
+
paginationClass?: string;
|
|
28
|
+
paginationTestId?: string;
|
|
29
|
+
perPage?: Search.PagnationProps['perPage'];
|
|
30
|
+
} = $props();
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
{#snippet itemSnippet(item: NonNullable<Search.RootProps['items']>[number])}
|
|
34
|
+
<span>{item.label}</span>
|
|
35
|
+
{/snippet}
|
|
36
|
+
|
|
37
|
+
<Search.Root {items} bind:value class={rootClass} data-testid={rootTestId}>
|
|
38
|
+
<Search.Input class={inputClass} aria-label={inputLabel} data-testid={inputTestId} />
|
|
39
|
+
<Search.List item={itemSnippet} class={listClass} data-testid={listTestId} />
|
|
40
|
+
<Search.Pagnation bind:perPage class={paginationClass} data-testid={paginationTestId} />
|
|
41
|
+
</Search.Root>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Search } from '../index.js';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
items: Search.RootProps['items'];
|
|
4
|
+
value: Search.RootProps['value'];
|
|
5
|
+
rootClass?: string;
|
|
6
|
+
rootTestId?: string;
|
|
7
|
+
inputClass?: string;
|
|
8
|
+
inputLabel?: string;
|
|
9
|
+
inputTestId?: string;
|
|
10
|
+
listClass?: string;
|
|
11
|
+
listTestId?: string;
|
|
12
|
+
paginationClass?: string;
|
|
13
|
+
paginationTestId?: string;
|
|
14
|
+
perPage?: Search.PagnationProps['perPage'];
|
|
15
|
+
};
|
|
16
|
+
declare const SearchPassthroughHarness: import("svelte").Component<$$ComponentProps, {}, "value" | "perPage">;
|
|
17
|
+
type SearchPassthroughHarness = ReturnType<typeof SearchPassthroughHarness>;
|
|
18
|
+
export default SearchPassthroughHarness;
|
|
19
|
+
//# sourceMappingURL=SearchPassthroughHarness.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchPassthroughHarness.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/search/combinations/SearchPassthroughHarness.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAEpC,KAAK,gBAAgB,GAAI;IACtB,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;CAC5C,CAAC;AAgCJ,QAAA,MAAM,wBAAwB,uEAAwC,CAAC;AACvE,KAAK,wBAAwB,GAAG,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC5E,eAAe,wBAAwB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searchPassthrough.comp.test.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/search/combinations/searchPassthrough.comp.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/svelte';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import SearchPassthroughHarness from './SearchPassthroughHarness.svelte';
|
|
4
|
+
const items = [
|
|
5
|
+
{ label: 'Alpha', value: 'alpha' },
|
|
6
|
+
{ label: 'Beta', value: 'beta' }
|
|
7
|
+
];
|
|
8
|
+
describe('Search passthrough props', () => {
|
|
9
|
+
it('forwards root class and data attributes', () => {
|
|
10
|
+
render(SearchPassthroughHarness, {
|
|
11
|
+
props: {
|
|
12
|
+
items,
|
|
13
|
+
value: items[0],
|
|
14
|
+
rootClass: 'root-class',
|
|
15
|
+
rootTestId: 'search-root'
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
const root = screen.getByTestId('search-root');
|
|
19
|
+
expect(root.className).toContain('root-class');
|
|
20
|
+
expect(root.getAttribute('data-search-root')).toBe('');
|
|
21
|
+
});
|
|
22
|
+
it('forwards input class and aria attributes', () => {
|
|
23
|
+
render(SearchPassthroughHarness, {
|
|
24
|
+
props: {
|
|
25
|
+
items,
|
|
26
|
+
value: items[0],
|
|
27
|
+
inputClass: 'input-class',
|
|
28
|
+
inputLabel: 'Filter items',
|
|
29
|
+
inputTestId: 'search-input'
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
const input = screen.getByTestId('search-input');
|
|
33
|
+
expect(input.className).toContain('input-class');
|
|
34
|
+
expect(input.getAttribute('aria-label')).toBe('Filter items');
|
|
35
|
+
expect(input.getAttribute('data-search-input')).toBe('');
|
|
36
|
+
});
|
|
37
|
+
it('forwards list class and data attributes to the list container', () => {
|
|
38
|
+
render(SearchPassthroughHarness, {
|
|
39
|
+
props: {
|
|
40
|
+
items,
|
|
41
|
+
value: items[0],
|
|
42
|
+
listClass: 'list-class',
|
|
43
|
+
listTestId: 'search-list'
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
const list = screen.getByTestId('search-list');
|
|
47
|
+
const itemButton = screen.getByRole('button', { name: 'Alpha' });
|
|
48
|
+
expect(list.className).toContain('list-class');
|
|
49
|
+
expect(list.getAttribute('data-search-list')).toBe('');
|
|
50
|
+
expect(itemButton.className).not.toContain('list-class');
|
|
51
|
+
});
|
|
52
|
+
it('forwards pagination class and data attributes to the pagination root', () => {
|
|
53
|
+
render(SearchPassthroughHarness, {
|
|
54
|
+
props: {
|
|
55
|
+
items,
|
|
56
|
+
value: items[0],
|
|
57
|
+
paginationClass: 'pagination-class',
|
|
58
|
+
paginationTestId: 'search-pagination'
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
const pagination = screen.getByTestId('search-pagination');
|
|
62
|
+
expect(pagination.className).toContain('pagination-class');
|
|
63
|
+
expect(pagination.getAttribute('data-search-item')).toBe('');
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searchPopover.comp.test.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/search/combinations/searchPopover.comp.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/svelte';
|
|
2
|
+
import { createRawSnippet } from 'svelte';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
import SearchPopover from './searchPopover.svelte';
|
|
5
|
+
const selectedItem = {
|
|
6
|
+
label: 'Alpha',
|
|
7
|
+
value: 'alpha'
|
|
8
|
+
};
|
|
9
|
+
const itemSnippet = createRawSnippet((getItem) => ({
|
|
10
|
+
render: () => `<span>${getItem().label}</span>`
|
|
11
|
+
}));
|
|
12
|
+
describe('SearchPopover', () => {
|
|
13
|
+
it('applies the consumer class to the real popover trigger button', () => {
|
|
14
|
+
render(SearchPopover, {
|
|
15
|
+
props: {
|
|
16
|
+
class: 'consumer-class',
|
|
17
|
+
item: itemSnippet,
|
|
18
|
+
items: [selectedItem],
|
|
19
|
+
value: selectedItem
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
const trigger = screen.getByRole('combobox');
|
|
23
|
+
expect(trigger.className).toContain('consumer-class');
|
|
24
|
+
});
|
|
25
|
+
it('forwards non-class trigger props to the real popover trigger button', () => {
|
|
26
|
+
render(SearchPopover, {
|
|
27
|
+
props: {
|
|
28
|
+
disabled: true,
|
|
29
|
+
item: itemSnippet,
|
|
30
|
+
items: [selectedItem],
|
|
31
|
+
value: selectedItem
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
expect(screen.getByRole('combobox')).toHaveProperty('disabled', true);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
<Popover.Trigger {...restProps}>
|
|
48
48
|
{#snippet child({ props })}
|
|
49
49
|
<Button
|
|
50
|
+
{...props}
|
|
50
51
|
bind:ref={trigger}
|
|
51
52
|
variant="outline"
|
|
52
|
-
class={cn('focus-ring h-fit w-full justify-between overflow-hidden', className)}
|
|
53
|
-
{...props}
|
|
53
|
+
class={cn(props.class as string, 'focus-ring h-fit w-full justify-between overflow-hidden', className)}
|
|
54
54
|
role="combobox"
|
|
55
55
|
>
|
|
56
56
|
{#if value}
|
|
@@ -8,7 +8,15 @@
|
|
|
8
8
|
import { useSearchList } from '../search.svelte';
|
|
9
9
|
import type { SearchListProps } from '../types';
|
|
10
10
|
|
|
11
|
-
let {
|
|
11
|
+
let {
|
|
12
|
+
children,
|
|
13
|
+
child,
|
|
14
|
+
class: className,
|
|
15
|
+
id = useId(),
|
|
16
|
+
ref = $bindable(null),
|
|
17
|
+
item,
|
|
18
|
+
...restProps
|
|
19
|
+
}: SearchListProps = $props();
|
|
12
20
|
|
|
13
21
|
const listState = useSearchList({
|
|
14
22
|
id: box.with(() => id),
|
|
@@ -24,7 +32,7 @@
|
|
|
24
32
|
{#if child}
|
|
25
33
|
{@render child({ props: mergedProps })}
|
|
26
34
|
{:else}
|
|
27
|
-
<div {...mergedProps} class=
|
|
35
|
+
<div {...mergedProps} class={cn('grid w-full auto-rows-fr px-2', className)}>
|
|
28
36
|
{#each listState.suggestions as listItem (listItem.value)}
|
|
29
37
|
<Button
|
|
30
38
|
variant="ghost"
|
|
@@ -34,8 +42,7 @@
|
|
|
34
42
|
hover:bg-muted
|
|
35
43
|
`,
|
|
36
44
|
listState.selected(listItem) && 'bg-muted',
|
|
37
|
-
listState.visible(listItem) ? '' : 'hidden!'
|
|
38
|
-
mergedProps.class
|
|
45
|
+
listState.visible(listItem) ? '' : 'hidden!'
|
|
39
46
|
)}
|
|
40
47
|
onclick={() => listState.select(listItem)}
|
|
41
48
|
>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-list.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/search/components/search-list.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"search-list.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/search/components/search-list.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAwDhD,QAAA,MAAM,UAAU,wDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
import { box, mergeProps } from 'svelte-toolbelt';
|
|
4
4
|
|
|
5
5
|
import * as Pagination from '$shadcn/pagination/index.js';
|
|
6
|
+
import { cn } from '../../../utils/index.js';
|
|
6
7
|
|
|
7
8
|
import { useSearchPagnation } from '../search.svelte';
|
|
8
9
|
import type { SearchPagnationProps } from '../types';
|
|
9
10
|
|
|
10
11
|
let {
|
|
11
12
|
child,
|
|
13
|
+
class: className,
|
|
12
14
|
id = useId(),
|
|
13
15
|
ref = $bindable(null),
|
|
14
16
|
page = $bindable(1),
|
|
@@ -40,7 +42,13 @@
|
|
|
40
42
|
{#if child}
|
|
41
43
|
{@render child({ props: mergedProps })}
|
|
42
44
|
{:else}
|
|
43
|
-
<Pagination.Root
|
|
45
|
+
<Pagination.Root
|
|
46
|
+
{...mergedProps}
|
|
47
|
+
class={cn('pb-2', className)}
|
|
48
|
+
count={pagnationState.length}
|
|
49
|
+
perPage={pagnationState.perPage}
|
|
50
|
+
bind:page
|
|
51
|
+
>
|
|
44
52
|
{#snippet children({ pages, currentPage })}
|
|
45
53
|
<Pagination.Content>
|
|
46
54
|
<Pagination.Item>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-pagnation.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/search/components/search-pagnation.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"search-pagnation.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/search/components/search-pagnation.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AA2ErD,QAAA,MAAM,eAAe;;;;8BAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite-plugin-component-source-collector.d.ts","sourceRoot":"","sources":["../../src/lib/vite/vite-plugin-component-source-collector.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAkB,MAAM,MAAM,CAAC;AAWnD,UAAU,OAAO;IACf;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAkID,wBAA8B,wBAAwB,CAAC,IAAI,GAAE,OAA8B,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"vite-plugin-component-source-collector.d.ts","sourceRoot":"","sources":["../../src/lib/vite/vite-plugin-component-source-collector.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAkB,MAAM,MAAM,CAAC;AAWnD,UAAU,OAAO;IACf;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAkID,wBAA8B,wBAAwB,CAAC,IAAI,GAAE,OAA8B,GAAG,OAAO,CAAC,MAAM,CAAC,CAyY5G"}
|
|
@@ -128,6 +128,8 @@ export default async function componentSourceCollector(opts = { safePackages: []
|
|
|
128
128
|
// style modules that Tailwind should never parse directly.
|
|
129
129
|
if (id.includes('?svelte&type=style'))
|
|
130
130
|
return false;
|
|
131
|
+
// TODO what about functions named eg transitionIconClass(t: number)...
|
|
132
|
+
// they dont get picked up cause no : or = after class
|
|
131
133
|
return classAttributeRegex.test(code);
|
|
132
134
|
}
|
|
133
135
|
async function normalizeCollectedSourceFilePath(file) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelte-ag",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "Useful svelte components",
|
|
5
5
|
"bugs": "https://github.com/ageorgeh/svelte-ag/issues",
|
|
6
6
|
"author": "Alexander Hornung",
|
|
@@ -82,12 +82,14 @@
|
|
|
82
82
|
"@sveltejs/vite-plugin-svelte": "^7.1.1",
|
|
83
83
|
"@tailwindcss/typography": "^0.5.19",
|
|
84
84
|
"@tailwindcss/vite": "^4.2.4",
|
|
85
|
+
"@testing-library/svelte": "^5.3.1",
|
|
85
86
|
"@types/node": "^24.12.2",
|
|
86
87
|
"@typescript/native-preview": "7.0.0-dev.20260506.1",
|
|
87
88
|
"eslint": "^10.3.0",
|
|
88
89
|
"eslint-plugin-better-tailwindcss": "^4.5.0",
|
|
89
90
|
"eslint-plugin-svelte": "^3.17.1",
|
|
90
91
|
"globals": "^17.6.0",
|
|
92
|
+
"happy-dom": "^20.9.0",
|
|
91
93
|
"husky": "^9.1.7",
|
|
92
94
|
"lint-staged": "^17.0.2",
|
|
93
95
|
"oxfmt": "^0.51.0",
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Search } from '../index.js';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
items,
|
|
6
|
+
value = $bindable(),
|
|
7
|
+
rootClass,
|
|
8
|
+
rootTestId,
|
|
9
|
+
inputClass,
|
|
10
|
+
inputLabel,
|
|
11
|
+
inputTestId,
|
|
12
|
+
listClass,
|
|
13
|
+
listTestId,
|
|
14
|
+
paginationClass,
|
|
15
|
+
paginationTestId,
|
|
16
|
+
perPage = $bindable(1)
|
|
17
|
+
}: {
|
|
18
|
+
items: Search.RootProps['items'];
|
|
19
|
+
value: Search.RootProps['value'];
|
|
20
|
+
rootClass?: string;
|
|
21
|
+
rootTestId?: string;
|
|
22
|
+
inputClass?: string;
|
|
23
|
+
inputLabel?: string;
|
|
24
|
+
inputTestId?: string;
|
|
25
|
+
listClass?: string;
|
|
26
|
+
listTestId?: string;
|
|
27
|
+
paginationClass?: string;
|
|
28
|
+
paginationTestId?: string;
|
|
29
|
+
perPage?: Search.PagnationProps['perPage'];
|
|
30
|
+
} = $props();
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
{#snippet itemSnippet(item: NonNullable<Search.RootProps['items']>[number])}
|
|
34
|
+
<span>{item.label}</span>
|
|
35
|
+
{/snippet}
|
|
36
|
+
|
|
37
|
+
<Search.Root {items} bind:value class={rootClass} data-testid={rootTestId}>
|
|
38
|
+
<Search.Input class={inputClass} aria-label={inputLabel} data-testid={inputTestId} />
|
|
39
|
+
<Search.List item={itemSnippet} class={listClass} data-testid={listTestId} />
|
|
40
|
+
<Search.Pagnation bind:perPage class={paginationClass} data-testid={paginationTestId} />
|
|
41
|
+
</Search.Root>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/svelte';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import SearchPassthroughHarness from './SearchPassthroughHarness.svelte';
|
|
5
|
+
|
|
6
|
+
const items = [
|
|
7
|
+
{ label: 'Alpha', value: 'alpha' },
|
|
8
|
+
{ label: 'Beta', value: 'beta' }
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
describe('Search passthrough props', () => {
|
|
12
|
+
it('forwards root class and data attributes', () => {
|
|
13
|
+
render(SearchPassthroughHarness, {
|
|
14
|
+
props: {
|
|
15
|
+
items,
|
|
16
|
+
value: items[0],
|
|
17
|
+
rootClass: 'root-class',
|
|
18
|
+
rootTestId: 'search-root'
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const root = screen.getByTestId('search-root');
|
|
23
|
+
|
|
24
|
+
expect(root.className).toContain('root-class');
|
|
25
|
+
expect(root.getAttribute('data-search-root')).toBe('');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('forwards input class and aria attributes', () => {
|
|
29
|
+
render(SearchPassthroughHarness, {
|
|
30
|
+
props: {
|
|
31
|
+
items,
|
|
32
|
+
value: items[0],
|
|
33
|
+
inputClass: 'input-class',
|
|
34
|
+
inputLabel: 'Filter items',
|
|
35
|
+
inputTestId: 'search-input'
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const input = screen.getByTestId('search-input');
|
|
40
|
+
|
|
41
|
+
expect(input.className).toContain('input-class');
|
|
42
|
+
expect(input.getAttribute('aria-label')).toBe('Filter items');
|
|
43
|
+
expect(input.getAttribute('data-search-input')).toBe('');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('forwards list class and data attributes to the list container', () => {
|
|
47
|
+
render(SearchPassthroughHarness, {
|
|
48
|
+
props: {
|
|
49
|
+
items,
|
|
50
|
+
value: items[0],
|
|
51
|
+
listClass: 'list-class',
|
|
52
|
+
listTestId: 'search-list'
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const list = screen.getByTestId('search-list');
|
|
57
|
+
const itemButton = screen.getByRole('button', { name: 'Alpha' });
|
|
58
|
+
|
|
59
|
+
expect(list.className).toContain('list-class');
|
|
60
|
+
expect(list.getAttribute('data-search-list')).toBe('');
|
|
61
|
+
expect(itemButton.className).not.toContain('list-class');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('forwards pagination class and data attributes to the pagination root', () => {
|
|
65
|
+
render(SearchPassthroughHarness, {
|
|
66
|
+
props: {
|
|
67
|
+
items,
|
|
68
|
+
value: items[0],
|
|
69
|
+
paginationClass: 'pagination-class',
|
|
70
|
+
paginationTestId: 'search-pagination'
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const pagination = screen.getByTestId('search-pagination');
|
|
75
|
+
|
|
76
|
+
expect(pagination.className).toContain('pagination-class');
|
|
77
|
+
expect(pagination.getAttribute('data-search-item')).toBe('');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/svelte';
|
|
2
|
+
import { createRawSnippet } from 'svelte';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
|
|
5
|
+
import SearchPopover from './searchPopover.svelte';
|
|
6
|
+
|
|
7
|
+
const selectedItem = {
|
|
8
|
+
label: 'Alpha',
|
|
9
|
+
value: 'alpha'
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const itemSnippet = createRawSnippet<[typeof selectedItem]>((getItem) => ({
|
|
13
|
+
render: () => `<span>${getItem().label}</span>`
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
describe('SearchPopover', () => {
|
|
17
|
+
it('applies the consumer class to the real popover trigger button', () => {
|
|
18
|
+
render(SearchPopover, {
|
|
19
|
+
props: {
|
|
20
|
+
class: 'consumer-class',
|
|
21
|
+
item: itemSnippet,
|
|
22
|
+
items: [selectedItem],
|
|
23
|
+
value: selectedItem
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const trigger = screen.getByRole('combobox');
|
|
28
|
+
|
|
29
|
+
expect(trigger.className).toContain('consumer-class');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('forwards non-class trigger props to the real popover trigger button', () => {
|
|
33
|
+
render(SearchPopover, {
|
|
34
|
+
props: {
|
|
35
|
+
disabled: true,
|
|
36
|
+
item: itemSnippet,
|
|
37
|
+
items: [selectedItem],
|
|
38
|
+
value: selectedItem
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(screen.getByRole('combobox')).toHaveProperty('disabled', true);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
<Popover.Trigger {...restProps}>
|
|
48
48
|
{#snippet child({ props })}
|
|
49
49
|
<Button
|
|
50
|
+
{...props}
|
|
50
51
|
bind:ref={trigger}
|
|
51
52
|
variant="outline"
|
|
52
|
-
class={cn('focus-ring h-fit w-full justify-between overflow-hidden', className)}
|
|
53
|
-
{...props}
|
|
53
|
+
class={cn(props.class as string, 'focus-ring h-fit w-full justify-between overflow-hidden', className)}
|
|
54
54
|
role="combobox"
|
|
55
55
|
>
|
|
56
56
|
{#if value}
|
|
@@ -8,7 +8,15 @@
|
|
|
8
8
|
import { useSearchList } from '../search.svelte';
|
|
9
9
|
import type { SearchListProps } from '../types';
|
|
10
10
|
|
|
11
|
-
let {
|
|
11
|
+
let {
|
|
12
|
+
children,
|
|
13
|
+
child,
|
|
14
|
+
class: className,
|
|
15
|
+
id = useId(),
|
|
16
|
+
ref = $bindable(null),
|
|
17
|
+
item,
|
|
18
|
+
...restProps
|
|
19
|
+
}: SearchListProps = $props();
|
|
12
20
|
|
|
13
21
|
const listState = useSearchList({
|
|
14
22
|
id: box.with(() => id),
|
|
@@ -24,7 +32,7 @@
|
|
|
24
32
|
{#if child}
|
|
25
33
|
{@render child({ props: mergedProps })}
|
|
26
34
|
{:else}
|
|
27
|
-
<div {...mergedProps} class=
|
|
35
|
+
<div {...mergedProps} class={cn('grid w-full auto-rows-fr px-2', className)}>
|
|
28
36
|
{#each listState.suggestions as listItem (listItem.value)}
|
|
29
37
|
<Button
|
|
30
38
|
variant="ghost"
|
|
@@ -34,8 +42,7 @@
|
|
|
34
42
|
hover:bg-muted
|
|
35
43
|
`,
|
|
36
44
|
listState.selected(listItem) && 'bg-muted',
|
|
37
|
-
listState.visible(listItem) ? '' : 'hidden!'
|
|
38
|
-
mergedProps.class
|
|
45
|
+
listState.visible(listItem) ? '' : 'hidden!'
|
|
39
46
|
)}
|
|
40
47
|
onclick={() => listState.select(listItem)}
|
|
41
48
|
>
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
import { box, mergeProps } from 'svelte-toolbelt';
|
|
4
4
|
|
|
5
5
|
import * as Pagination from '$shadcn/pagination/index.js';
|
|
6
|
+
import { cn } from '$utils/index.js';
|
|
6
7
|
|
|
7
8
|
import { useSearchPagnation } from '../search.svelte';
|
|
8
9
|
import type { SearchPagnationProps } from '../types';
|
|
9
10
|
|
|
10
11
|
let {
|
|
11
12
|
child,
|
|
13
|
+
class: className,
|
|
12
14
|
id = useId(),
|
|
13
15
|
ref = $bindable(null),
|
|
14
16
|
page = $bindable(1),
|
|
@@ -40,7 +42,13 @@
|
|
|
40
42
|
{#if child}
|
|
41
43
|
{@render child({ props: mergedProps })}
|
|
42
44
|
{:else}
|
|
43
|
-
<Pagination.Root
|
|
45
|
+
<Pagination.Root
|
|
46
|
+
{...mergedProps}
|
|
47
|
+
class={cn('pb-2', className)}
|
|
48
|
+
count={pagnationState.length}
|
|
49
|
+
perPage={pagnationState.perPage}
|
|
50
|
+
bind:page
|
|
51
|
+
>
|
|
44
52
|
{#snippet children({ pages, currentPage })}
|
|
45
53
|
<Pagination.Content>
|
|
46
54
|
<Pagination.Item>
|
|
@@ -180,6 +180,9 @@ export default async function componentSourceCollector(opts: Options = { safePac
|
|
|
180
180
|
// a Tailwind source signal. Including those files can pull in component-local
|
|
181
181
|
// style modules that Tailwind should never parse directly.
|
|
182
182
|
if (id.includes('?svelte&type=style')) return false;
|
|
183
|
+
|
|
184
|
+
// TODO what about functions named eg transitionIconClass(t: number)...
|
|
185
|
+
// they dont get picked up cause no : or = after class
|
|
183
186
|
return classAttributeRegex.test(code);
|
|
184
187
|
}
|
|
185
188
|
|