@dillingerstaffing/strand-svelte 0.4.1

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 (105) hide show
  1. package/README.md +56 -0
  2. package/dist/css/strand-ui.css +2583 -0
  3. package/dist/index.js +4154 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +53 -0
  6. package/src/components/Alert/Alert.svelte +32 -0
  7. package/src/components/Alert/Alert.test.ts +64 -0
  8. package/src/components/Alert/index.ts +2 -0
  9. package/src/components/Avatar/Avatar.svelte +40 -0
  10. package/src/components/Avatar/Avatar.test.ts +55 -0
  11. package/src/components/Avatar/index.ts +2 -0
  12. package/src/components/Badge/Badge.svelte +41 -0
  13. package/src/components/Badge/Badge.test.ts +55 -0
  14. package/src/components/Badge/index.ts +2 -0
  15. package/src/components/Breadcrumb/Breadcrumb.svelte +29 -0
  16. package/src/components/Breadcrumb/Breadcrumb.test.ts +66 -0
  17. package/src/components/Breadcrumb/index.ts +2 -0
  18. package/src/components/Button/Button.svelte +55 -0
  19. package/src/components/Button/Button.test.ts +110 -0
  20. package/src/components/Button/index.ts +2 -0
  21. package/src/components/Card/Card.svelte +17 -0
  22. package/src/components/Card/Card.test.ts +32 -0
  23. package/src/components/Card/index.ts +2 -0
  24. package/src/components/Checkbox/Checkbox.svelte +62 -0
  25. package/src/components/Checkbox/Checkbox.test.ts +67 -0
  26. package/src/components/Checkbox/index.ts +2 -0
  27. package/src/components/CodeBlock/CodeBlock.svelte +14 -0
  28. package/src/components/CodeBlock/CodeBlock.test.ts +36 -0
  29. package/src/components/CodeBlock/index.ts +2 -0
  30. package/src/components/Container/Container.svelte +14 -0
  31. package/src/components/Container/Container.test.ts +23 -0
  32. package/src/components/Container/index.ts +2 -0
  33. package/src/components/DataReadout/DataReadout.svelte +19 -0
  34. package/src/components/DataReadout/DataReadout.test.ts +35 -0
  35. package/src/components/DataReadout/index.ts +2 -0
  36. package/src/components/Dialog/Dialog.svelte +131 -0
  37. package/src/components/Dialog/Dialog.test.ts +77 -0
  38. package/src/components/Dialog/index.ts +2 -0
  39. package/src/components/Divider/Divider.svelte +36 -0
  40. package/src/components/Divider/Divider.test.ts +34 -0
  41. package/src/components/Divider/index.ts +2 -0
  42. package/src/components/FormField/FormField.svelte +39 -0
  43. package/src/components/FormField/FormField.test.ts +58 -0
  44. package/src/components/FormField/index.ts +2 -0
  45. package/src/components/Grid/Grid.svelte +13 -0
  46. package/src/components/Grid/Grid.test.ts +32 -0
  47. package/src/components/Grid/index.ts +2 -0
  48. package/src/components/Input/Input.svelte +41 -0
  49. package/src/components/Input/Input.test.ts +64 -0
  50. package/src/components/Input/index.ts +2 -0
  51. package/src/components/Link/Link.svelte +17 -0
  52. package/src/components/Link/Link.test.ts +28 -0
  53. package/src/components/Link/index.ts +2 -0
  54. package/src/components/Nav/Nav.svelte +69 -0
  55. package/src/components/Nav/Nav.test.ts +75 -0
  56. package/src/components/Nav/index.ts +2 -0
  57. package/src/components/Progress/Progress.svelte +78 -0
  58. package/src/components/Progress/Progress.test.ts +58 -0
  59. package/src/components/Progress/index.ts +2 -0
  60. package/src/components/Radio/Radio.svelte +46 -0
  61. package/src/components/Radio/Radio.test.ts +52 -0
  62. package/src/components/Radio/index.ts +2 -0
  63. package/src/components/Section/Section.svelte +17 -0
  64. package/src/components/Section/Section.test.ts +29 -0
  65. package/src/components/Section/index.ts +2 -0
  66. package/src/components/Select/Select.svelte +45 -0
  67. package/src/components/Select/Select.test.ts +59 -0
  68. package/src/components/Select/index.ts +2 -0
  69. package/src/components/Skeleton/Skeleton.svelte +25 -0
  70. package/src/components/Skeleton/Skeleton.test.ts +44 -0
  71. package/src/components/Skeleton/index.ts +2 -0
  72. package/src/components/Slider/Slider.svelte +37 -0
  73. package/src/components/Slider/Slider.test.ts +45 -0
  74. package/src/components/Slider/index.ts +2 -0
  75. package/src/components/Spinner/Spinner.svelte +15 -0
  76. package/src/components/Spinner/Spinner.test.ts +38 -0
  77. package/src/components/Spinner/index.ts +2 -0
  78. package/src/components/Stack/Stack.svelte +27 -0
  79. package/src/components/Stack/Stack.test.ts +46 -0
  80. package/src/components/Stack/index.ts +2 -0
  81. package/src/components/Switch/Switch.svelte +48 -0
  82. package/src/components/Switch/Switch.test.ts +61 -0
  83. package/src/components/Switch/index.ts +2 -0
  84. package/src/components/Table/Table.svelte +67 -0
  85. package/src/components/Table/Table.test.ts +88 -0
  86. package/src/components/Table/index.ts +2 -0
  87. package/src/components/Tabs/Tabs.svelte +89 -0
  88. package/src/components/Tabs/Tabs.test.ts +66 -0
  89. package/src/components/Tabs/index.ts +2 -0
  90. package/src/components/Tag/Tag.svelte +33 -0
  91. package/src/components/Tag/Tag.test.ts +63 -0
  92. package/src/components/Tag/index.ts +2 -0
  93. package/src/components/Textarea/Textarea.svelte +53 -0
  94. package/src/components/Textarea/Textarea.test.ts +53 -0
  95. package/src/components/Textarea/index.ts +2 -0
  96. package/src/components/Toast/Toast.svelte +29 -0
  97. package/src/components/Toast/Toast.test.ts +60 -0
  98. package/src/components/Toast/ToastProvider.svelte +45 -0
  99. package/src/components/Toast/index.ts +5 -0
  100. package/src/components/Toast/useToast.ts +78 -0
  101. package/src/components/Tooltip/Tooltip.svelte +56 -0
  102. package/src/components/Tooltip/Tooltip.test.ts +50 -0
  103. package/src/components/Tooltip/index.ts +2 -0
  104. package/src/index.ts +46 -0
  105. package/src/test-setup.ts +7 -0
@@ -0,0 +1,78 @@
1
+ /*! Strand Svelte | MIT License | dillingerstaffing.com */
2
+
3
+ import { getContext, setContext } from 'svelte'
4
+ import { writable } from 'svelte/store'
5
+ import type { Writable } from 'svelte/store'
6
+
7
+ export type ToastStatus = 'info' | 'success' | 'warning' | 'error'
8
+
9
+ export interface ToastOptions {
10
+ message: string
11
+ status?: ToastStatus
12
+ duration?: number
13
+ }
14
+
15
+ export interface ToastEntry {
16
+ id: number
17
+ message: string
18
+ status: ToastStatus
19
+ duration: number
20
+ }
21
+
22
+ export interface ToastContextValue {
23
+ toasts: Writable<ToastEntry[]>
24
+ toast: (options: ToastOptions) => void
25
+ removeToast: (id: number) => void
26
+ }
27
+
28
+ const TOAST_KEY = Symbol('StrandToast')
29
+
30
+ let toastIdCounter = 0
31
+
32
+ export function createToastContext(): ToastContextValue {
33
+ const toasts = writable<ToastEntry[]>([])
34
+ const timers = new Map<number, ReturnType<typeof setTimeout>>()
35
+
36
+ function removeToast(id: number) {
37
+ const timer = timers.get(id)
38
+ if (timer !== undefined) {
39
+ clearTimeout(timer)
40
+ timers.delete(id)
41
+ }
42
+ toasts.update((prev) => prev.filter((t) => t.id !== id))
43
+ }
44
+
45
+ function addToast(options: ToastOptions) {
46
+ const entry: ToastEntry = {
47
+ id: ++toastIdCounter,
48
+ message: options.message,
49
+ status: options.status ?? 'info',
50
+ duration: options.duration ?? 5000,
51
+ }
52
+ toasts.update((prev) => [...prev, entry])
53
+
54
+ if (entry.duration > 0) {
55
+ const timer = setTimeout(() => {
56
+ removeToast(entry.id)
57
+ }, entry.duration)
58
+ timers.set(entry.id, timer)
59
+ }
60
+ }
61
+
62
+ const ctx: ToastContextValue = {
63
+ toasts,
64
+ toast: addToast,
65
+ removeToast,
66
+ }
67
+
68
+ setContext(TOAST_KEY, ctx)
69
+ return ctx
70
+ }
71
+
72
+ export function getToastContext(): ToastContextValue {
73
+ const ctx = getContext<ToastContextValue>(TOAST_KEY)
74
+ if (!ctx) {
75
+ throw new Error('getToastContext must be used within a ToastProvider')
76
+ }
77
+ return ctx
78
+ }
@@ -0,0 +1,56 @@
1
+ <!--! Strand Svelte | MIT License | dillingerstaffing.com -->
2
+ <script lang="ts">
3
+ /** Tooltip text */
4
+ export let content: string
5
+ /** Position relative to trigger */
6
+ export let position: 'top' | 'right' | 'bottom' | 'left' = 'top'
7
+ /** Delay in ms before showing */
8
+ export let delay: number = 200
9
+
10
+ let visible = false
11
+ let timer: ReturnType<typeof setTimeout> | null = null
12
+
13
+ let tooltipIdCounter = 0
14
+ const tooltipId = `strand-tooltip-${++tooltipIdCounter}`
15
+
16
+ function show() {
17
+ timer = setTimeout(() => {
18
+ visible = true
19
+ }, delay)
20
+ }
21
+
22
+ function hide() {
23
+ if (timer !== null) {
24
+ clearTimeout(timer)
25
+ timer = null
26
+ }
27
+ visible = false
28
+ }
29
+
30
+ $: tooltipClasses = [
31
+ 'strand-tooltip',
32
+ `strand-tooltip--${position}`,
33
+ visible && 'strand-tooltip--visible',
34
+ ].filter(Boolean).join(' ')
35
+ </script>
36
+
37
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
38
+ <span
39
+ class="strand-tooltip__wrapper"
40
+ on:mouseenter={show}
41
+ on:mouseleave={hide}
42
+ on:focus={show}
43
+ on:blur={hide}
44
+ aria-describedby={tooltipId}
45
+ {...$$restProps}
46
+ >
47
+ <slot />
48
+ <span
49
+ id={tooltipId}
50
+ class={tooltipClasses}
51
+ role="tooltip"
52
+ aria-hidden={!visible}
53
+ >
54
+ {content}
55
+ </span>
56
+ </span>
@@ -0,0 +1,50 @@
1
+ /*! Strand Svelte | MIT License | dillingerstaffing.com */
2
+
3
+ import { describe, it, expect } from 'vitest'
4
+ import { render } from '@testing-library/svelte'
5
+ import Tooltip from './Tooltip.svelte'
6
+
7
+ describe('Tooltip', () => {
8
+ it('renders wrapper with aria-describedby', () => {
9
+ const { container } = render(Tooltip, { props: { content: 'Help text' } })
10
+ const wrapper = container.querySelector('.strand-tooltip__wrapper')
11
+ expect(wrapper).toBeInTheDocument()
12
+ expect(wrapper).toHaveAttribute('aria-describedby')
13
+ })
14
+
15
+ it('renders tooltip element with role', () => {
16
+ const { container } = render(Tooltip, { props: { content: 'Help text' } })
17
+ const tip = container.querySelector('[role="tooltip"]')
18
+ expect(tip).toBeInTheDocument()
19
+ expect(tip).toHaveTextContent('Help text')
20
+ expect(tip).toHaveClass('strand-tooltip')
21
+ })
22
+
23
+ it('applies position class', () => {
24
+ const positions = ['top', 'right', 'bottom', 'left'] as const
25
+ for (const position of positions) {
26
+ const { container, unmount } = render(Tooltip, { props: { content: 'Tip', position } })
27
+ expect(container.querySelector('.strand-tooltip')).toHaveClass(`strand-tooltip--${position}`)
28
+ unmount()
29
+ }
30
+ })
31
+
32
+ it('is hidden by default', () => {
33
+ const { container } = render(Tooltip, { props: { content: 'Tip' } })
34
+ const tip = container.querySelector('.strand-tooltip')
35
+ expect(tip).toHaveAttribute('aria-hidden', 'true')
36
+ expect(tip).not.toHaveClass('strand-tooltip--visible')
37
+ })
38
+
39
+ it('tooltip id matches aria-describedby', () => {
40
+ const { container } = render(Tooltip, { props: { content: 'Tip' } })
41
+ const wrapper = container.querySelector('.strand-tooltip__wrapper')
42
+ const tip = container.querySelector('[role="tooltip"]')
43
+ expect(wrapper!.getAttribute('aria-describedby')).toBe(tip!.id)
44
+ })
45
+
46
+ it('defaults to top position', () => {
47
+ const { container } = render(Tooltip, { props: { content: 'Tip' } })
48
+ expect(container.querySelector('.strand-tooltip')).toHaveClass('strand-tooltip--top')
49
+ })
50
+ })
@@ -0,0 +1,2 @@
1
+ /*! Strand Svelte | MIT License | dillingerstaffing.com */
2
+ export { default as Tooltip } from './Tooltip.svelte'
package/src/index.ts ADDED
@@ -0,0 +1,46 @@
1
+ /*! Strand Svelte | MIT License | dillingerstaffing.com */
2
+
3
+ // Input
4
+ export { default as Button } from './components/Button/Button.svelte'
5
+ export { default as Input } from './components/Input/Input.svelte'
6
+ export { default as Textarea } from './components/Textarea/Textarea.svelte'
7
+ export { default as Select } from './components/Select/Select.svelte'
8
+ export { default as Checkbox } from './components/Checkbox/Checkbox.svelte'
9
+ export { default as Radio } from './components/Radio/Radio.svelte'
10
+ export { default as Switch } from './components/Switch/Switch.svelte'
11
+ export { default as Slider } from './components/Slider/Slider.svelte'
12
+ export { default as FormField } from './components/FormField/FormField.svelte'
13
+
14
+ // Display
15
+ export { default as Card } from './components/Card/Card.svelte'
16
+ export { default as Badge } from './components/Badge/Badge.svelte'
17
+ export { default as Avatar } from './components/Avatar/Avatar.svelte'
18
+ export { default as Tag } from './components/Tag/Tag.svelte'
19
+ export { default as Table } from './components/Table/Table.svelte'
20
+ export { default as DataReadout } from './components/DataReadout/DataReadout.svelte'
21
+ export { default as CodeBlock } from './components/CodeBlock/CodeBlock.svelte'
22
+
23
+ // Layout
24
+ export { default as Stack } from './components/Stack/Stack.svelte'
25
+ export { default as Grid } from './components/Grid/Grid.svelte'
26
+ export { default as Container } from './components/Container/Container.svelte'
27
+ export { default as Divider } from './components/Divider/Divider.svelte'
28
+ export { default as Section } from './components/Section/Section.svelte'
29
+
30
+ // Navigation
31
+ export { default as Link } from './components/Link/Link.svelte'
32
+ export { default as Tabs } from './components/Tabs/Tabs.svelte'
33
+ export { default as Breadcrumb } from './components/Breadcrumb/Breadcrumb.svelte'
34
+ export { default as Nav } from './components/Nav/Nav.svelte'
35
+
36
+ // Feedback
37
+ export { default as Toast } from './components/Toast/Toast.svelte'
38
+ export { default as ToastProvider } from './components/Toast/ToastProvider.svelte'
39
+ export { createToastContext, getToastContext } from './components/Toast/useToast'
40
+ export type { ToastStatus, ToastOptions, ToastEntry, ToastContextValue } from './components/Toast/useToast'
41
+ export { default as Alert } from './components/Alert/Alert.svelte'
42
+ export { default as Dialog } from './components/Dialog/Dialog.svelte'
43
+ export { default as Tooltip } from './components/Tooltip/Tooltip.svelte'
44
+ export { default as Progress } from './components/Progress/Progress.svelte'
45
+ export { default as Spinner } from './components/Spinner/Spinner.svelte'
46
+ export { default as Skeleton } from './components/Skeleton/Skeleton.svelte'
@@ -0,0 +1,7 @@
1
+ import "@testing-library/jest-dom/vitest";
2
+ import { cleanup } from "@testing-library/svelte";
3
+ import { afterEach } from "vitest";
4
+
5
+ afterEach(() => {
6
+ cleanup();
7
+ });