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.
Files changed (24) hide show
  1. package/dist/components/search/combinations/SearchPassthroughHarness.svelte +41 -0
  2. package/dist/components/search/combinations/SearchPassthroughHarness.svelte.d.ts +19 -0
  3. package/dist/components/search/combinations/SearchPassthroughHarness.svelte.d.ts.map +1 -0
  4. package/dist/components/search/combinations/searchPassthrough.comp.test.d.ts +2 -0
  5. package/dist/components/search/combinations/searchPassthrough.comp.test.d.ts.map +1 -0
  6. package/dist/components/search/combinations/searchPassthrough.comp.test.js +65 -0
  7. package/dist/components/search/combinations/searchPopover.comp.test.d.ts +2 -0
  8. package/dist/components/search/combinations/searchPopover.comp.test.d.ts.map +1 -0
  9. package/dist/components/search/combinations/searchPopover.comp.test.js +36 -0
  10. package/dist/components/search/combinations/searchPopover.svelte +2 -2
  11. package/dist/components/search/components/search-list.svelte +11 -4
  12. package/dist/components/search/components/search-list.svelte.d.ts.map +1 -1
  13. package/dist/components/search/components/search-pagnation.svelte +9 -1
  14. package/dist/components/search/components/search-pagnation.svelte.d.ts.map +1 -1
  15. package/dist/vite/vite-plugin-component-source-collector.d.ts.map +1 -1
  16. package/dist/vite/vite-plugin-component-source-collector.js +2 -0
  17. package/package.json +3 -1
  18. package/src/lib/components/search/combinations/SearchPassthroughHarness.svelte +41 -0
  19. package/src/lib/components/search/combinations/searchPassthrough.comp.test.ts +79 -0
  20. package/src/lib/components/search/combinations/searchPopover.comp.test.ts +44 -0
  21. package/src/lib/components/search/combinations/searchPopover.svelte +2 -2
  22. package/src/lib/components/search/components/search-list.svelte +11 -4
  23. package/src/lib/components/search/components/search-pagnation.svelte +9 -1
  24. 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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=searchPassthrough.comp.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=searchPopover.comp.test.d.ts.map
@@ -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 { children, child, id = useId(), ref = $bindable(null), item, ...restProps }: SearchListProps = $props();
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="grid w-full auto-rows-fr px-2">
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;AAiDhD,QAAA,MAAM,UAAU,wDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,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 class="pb-2" count={pagnationState.length} perPage={pagnationState.perPage} bind:page>
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":"AASA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAyErD,QAAA,MAAM,eAAe;;;;8BAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
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,CAsY5G"}
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.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 { children, child, id = useId(), ref = $bindable(null), item, ...restProps }: SearchListProps = $props();
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="grid w-full auto-rows-fr px-2">
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 class="pb-2" count={pagnationState.length} perPage={pagnationState.perPage} bind:page>
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