@regardio/react 0.4.7 → 0.5.5

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 (172) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +6 -8
  3. package/dist/{components/background-slideshow.js → background-slideshow/index.js} +2 -11
  4. package/dist/{components/blurry-gradient.js → blurry-gradient/index.js} +15 -9
  5. package/dist/{components/carousel.d.ts → carousel/index.d.ts} +17 -9
  6. package/dist/{components/carousel.js → carousel/index.js} +34 -30
  7. package/dist/{components/countdown.js → countdown/index.js} +2 -11
  8. package/dist/{components/generic-error.js → generic-error/index.js} +1 -1
  9. package/dist/grid/index.d.ts +1196 -0
  10. package/dist/grid/index.js +239 -0
  11. package/dist/heading/index.d.ts +24 -0
  12. package/dist/{components/heading.js → heading/index.js} +15 -34
  13. package/dist/highlight/index.d.ts +13 -0
  14. package/dist/{components/highlight.js → highlight/index.js} +9 -17
  15. package/dist/hooks/use-nonce.d.ts +1 -6
  16. package/dist/hooks/use-nonce.js +1 -6
  17. package/dist/{components/icon-button.js → icon-button/index.js} +1 -1
  18. package/dist/{components/if.js → if/index.js} +1 -1
  19. package/dist/{components/iframe.js → iframe/index.js} +2 -11
  20. package/dist/{components/link.d.ts → link/index.d.ts} +19 -13
  21. package/dist/{components/link.js → link/index.js} +31 -36
  22. package/dist/list/index.d.ts +69 -0
  23. package/dist/list/index.js +65 -0
  24. package/dist/{components/markdown-container.js → markdown-container/index.js} +3 -67
  25. package/dist/{components/password-input.js → password-input/index.js} +2 -11
  26. package/dist/{components/picture.js → picture/index.js} +2 -11
  27. package/dist/{components/protected-email.d.ts → protected-email/index.d.ts} +1 -1
  28. package/dist/{components/protected-email.js → protected-email/index.js} +1 -1
  29. package/dist/text/index.d.ts +20 -0
  30. package/dist/text/index.js +38 -0
  31. package/dist/utils/author/index.d.ts +3 -0
  32. package/dist/utils/author/index.js +33 -0
  33. package/dist/utils/text/index.d.ts +15 -0
  34. package/dist/utils/text/index.js +73 -0
  35. package/package.json +170 -187
  36. package/src/background-slideshow/background-slideshow.stories.tsx +137 -0
  37. package/src/{components → background-slideshow}/background-slideshow.tsx +3 -1
  38. package/src/background-slideshow/index.ts +2 -0
  39. package/src/{stories/BlurryGradient.stories.tsx → blurry-gradient/blurry-gradient.stories.tsx} +1 -1
  40. package/src/{components → blurry-gradient}/blurry-gradient.tsx +14 -8
  41. package/src/blurry-gradient/index.ts +2 -0
  42. package/src/carousel/carousel-content.tsx +16 -0
  43. package/src/carousel/carousel-item.tsx +23 -0
  44. package/src/carousel/carousel-next.tsx +22 -0
  45. package/src/carousel/carousel-previous.tsx +22 -0
  46. package/src/{components/carousel.tsx → carousel/carousel-root.tsx} +8 -78
  47. package/src/carousel/carousel.stories.tsx +135 -0
  48. package/src/carousel/index.parts.ts +5 -0
  49. package/src/carousel/index.ts +4 -0
  50. package/src/{stories/Countdown.stories.tsx → countdown/countdown.stories.tsx} +1 -1
  51. package/src/{components → countdown}/countdown.tsx +3 -7
  52. package/src/countdown/index.ts +1 -0
  53. package/src/{stories/GenericError.stories.tsx → generic-error/generic-error.stories.tsx} +1 -1
  54. package/src/{components → generic-error}/generic-error.tsx +2 -0
  55. package/src/generic-error/index.ts +2 -0
  56. package/src/grid/grid-item.tsx +188 -0
  57. package/src/grid/grid-root.tsx +72 -0
  58. package/src/grid/grid.stories.tsx +236 -0
  59. package/src/grid/index.parts.ts +2 -0
  60. package/src/grid/index.ts +5 -0
  61. package/src/{stories/Heading.stories.tsx → heading/heading.stories.tsx} +1 -1
  62. package/src/{components → heading}/heading.tsx +17 -25
  63. package/src/heading/index.ts +2 -0
  64. package/src/{stories/Highlight.stories.tsx → highlight/highlight.stories.tsx} +1 -1
  65. package/src/{components → highlight}/highlight.tsx +13 -9
  66. package/src/highlight/index.ts +2 -0
  67. package/src/hooks/use-nonce.ts +0 -10
  68. package/src/{stories/IconButton.stories.tsx → icon-button/icon-button.stories.tsx} +1 -1
  69. package/src/icon-button/index.ts +2 -0
  70. package/src/{stories/If.stories.tsx → if/if.stories.tsx} +1 -1
  71. package/src/if/index.ts +1 -0
  72. package/src/{stories/Iframe.stories.tsx → iframe/iframe.stories.tsx} +1 -1
  73. package/src/{components → iframe}/iframe.tsx +1 -1
  74. package/src/iframe/index.ts +2 -0
  75. package/src/link/index.ts +2 -0
  76. package/src/{stories/Link.stories.tsx → link/link.stories.tsx} +52 -1
  77. package/src/{components → link}/link.tsx +39 -28
  78. package/src/list/index.parts.ts +2 -0
  79. package/src/list/index.ts +4 -0
  80. package/src/list/list-item.tsx +63 -0
  81. package/src/list/list-root-context.ts +21 -0
  82. package/src/list/list-root.tsx +81 -0
  83. package/src/list/list.css +32 -0
  84. package/src/list/list.stories.tsx +119 -0
  85. package/src/list/list.test.tsx +168 -0
  86. package/src/markdown-container/index.ts +2 -0
  87. package/src/{stories/MarkdownContainer.stories.tsx → markdown-container/markdown-container.stories.tsx} +56 -1
  88. package/src/{components → markdown-container}/markdown-container.tsx +3 -1
  89. package/src/password-input/index.ts +2 -0
  90. package/src/{stories/PasswordInput.stories.tsx → password-input/password-input.stories.tsx} +1 -1
  91. package/src/{components → password-input}/password-input.tsx +4 -4
  92. package/src/picture/index.ts +2 -0
  93. package/src/{stories/Picture.stories.tsx → picture/picture.stories.tsx} +1 -1
  94. package/src/{components → picture}/picture.tsx +2 -4
  95. package/src/protected-email/index.ts +2 -0
  96. package/src/{stories/ProtectedEmail.stories.tsx → protected-email/protected-email.stories.tsx} +1 -1
  97. package/src/{components → protected-email}/protected-email.tsx +3 -1
  98. package/src/tailwind.css +10 -0
  99. package/src/text/index.ts +2 -0
  100. package/src/{stories/Text.stories.tsx → text/text.stories.tsx} +1 -1
  101. package/src/text/text.tsx +46 -0
  102. package/src/utils/author/author.tsx +36 -0
  103. package/src/utils/author/index.ts +1 -0
  104. package/src/utils/text/index.ts +1 -0
  105. package/src/utils/text/text.tsx +103 -0
  106. package/dist/components/box.d.ts +0 -20
  107. package/dist/components/box.js +0 -50
  108. package/dist/components/definition-list.d.ts +0 -43
  109. package/dist/components/definition-list.js +0 -89
  110. package/dist/components/heading.d.ts +0 -27
  111. package/dist/components/highlight.d.ts +0 -19
  112. package/dist/components/item.d.ts +0 -70
  113. package/dist/components/item.js +0 -512
  114. package/dist/components/leaflet-map.d.ts +0 -34
  115. package/dist/components/leaflet-map.js +0 -201
  116. package/dist/components/list-item.d.ts +0 -19
  117. package/dist/components/list-item.js +0 -37
  118. package/dist/components/maptiler-map.d.ts +0 -27
  119. package/dist/components/maptiler-map.js +0 -129
  120. package/dist/components/text.d.ts +0 -20
  121. package/dist/components/text.js +0 -45
  122. package/dist/components/unordered-list.d.ts +0 -19
  123. package/dist/components/unordered-list.js +0 -39
  124. package/dist/utils/author.d.ts +0 -9
  125. package/dist/utils/author.js +0 -55
  126. package/dist/utils/cn.d.ts +0 -9
  127. package/dist/utils/cn.js +0 -14
  128. package/dist/utils/is-route-active.d.ts +0 -19
  129. package/dist/utils/is-route-active.js +0 -56
  130. package/dist/utils/text.d.ts +0 -24
  131. package/dist/utils/text.js +0 -127
  132. package/src/components/box.tsx +0 -45
  133. package/src/components/definition-list.tsx +0 -90
  134. package/src/components/item.tsx +0 -340
  135. package/src/components/leaflet-map.tsx +0 -294
  136. package/src/components/link.test.tsx +0 -387
  137. package/src/components/list-item.tsx +0 -30
  138. package/src/components/maptiler-map.tsx +0 -181
  139. package/src/components/text.tsx +0 -38
  140. package/src/components/unordered-list.tsx +0 -32
  141. package/src/hooks/use-nonce.test.ts +0 -35
  142. package/src/stories/BackgroundSlideshow.stories.tsx +0 -68
  143. package/src/stories/Box.stories.tsx +0 -83
  144. package/src/stories/Carousel.stories.tsx +0 -95
  145. package/src/stories/DefinitionList.stories.tsx +0 -51
  146. package/src/stories/Item.stories.tsx +0 -79
  147. package/src/stories/ListItem.stories.tsx +0 -38
  148. package/src/stories/UnorderedList.stories.tsx +0 -73
  149. package/src/styles/tailwind.css +0 -7
  150. package/src/test-setup.ts +0 -1
  151. package/src/utils/author.test.ts +0 -54
  152. package/src/utils/author.tsx +0 -73
  153. package/src/utils/cn.test.ts +0 -48
  154. package/src/utils/cn.ts +0 -14
  155. package/src/utils/is-route-active.test.ts +0 -80
  156. package/src/utils/is-route-active.ts +0 -100
  157. package/src/utils/text.test.ts +0 -152
  158. package/src/utils/text.tsx +0 -209
  159. package/src/vite-env.d.ts +0 -1
  160. /package/dist/{components/background-slideshow.d.ts → background-slideshow/index.d.ts} +0 -0
  161. /package/dist/{components/blurry-gradient.d.ts → blurry-gradient/index.d.ts} +0 -0
  162. /package/dist/{components/countdown.d.ts → countdown/index.d.ts} +0 -0
  163. /package/dist/{components/generic-error.d.ts → generic-error/index.d.ts} +0 -0
  164. /package/dist/{components/icon-button.d.ts → icon-button/index.d.ts} +0 -0
  165. /package/dist/{components/if.d.ts → if/index.d.ts} +0 -0
  166. /package/dist/{components/iframe.d.ts → iframe/index.d.ts} +0 -0
  167. /package/dist/{components/markdown-container.d.ts → markdown-container/index.d.ts} +0 -0
  168. /package/dist/{components/password-input.d.ts → password-input/index.d.ts} +0 -0
  169. /package/dist/{components/picture.d.ts → picture/index.d.ts} +0 -0
  170. /package/src/{components → icon-button}/icon-button.tsx +0 -0
  171. /package/src/{components → if}/if.tsx +0 -0
  172. /package/src/{styles/storybook.css → storybook.css} +0 -0
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+
3
+ import { createContext, useContext } from 'react';
4
+
5
+ export interface ListRootContextValue {
6
+ /**
7
+ * Default element type for list items.
8
+ * @default 'li'
9
+ */
10
+ defaultItemElement: 'li' | 'dd' | 'dt' | 'div' | 'span';
11
+ /**
12
+ * Default className to apply to all list items.
13
+ */
14
+ defaultItemClassName?: string;
15
+ }
16
+
17
+ export const ListRootContext = createContext<ListRootContextValue | undefined>(undefined);
18
+
19
+ export function useListRootContext(): ListRootContextValue | undefined {
20
+ return useContext(ListRootContext);
21
+ }
@@ -0,0 +1,81 @@
1
+ 'use client';
2
+
3
+ import {
4
+ type ComponentPropsWithoutRef,
5
+ type ElementType,
6
+ type ForwardedRef,
7
+ forwardRef,
8
+ type ReactNode,
9
+ useMemo,
10
+ } from 'react';
11
+ import { ListRootContext, type ListRootContextValue } from './list-root-context';
12
+
13
+ type ListRootElement = 'ul' | 'ol' | 'dl' | 'div' | 'menu' | 'nav';
14
+
15
+ export type ListRootProps<T extends ListRootElement = 'ul'> = Omit<
16
+ ComponentPropsWithoutRef<T>,
17
+ 'children'
18
+ > & {
19
+ /**
20
+ * The element type to render.
21
+ * @default 'ul'
22
+ */
23
+ render?: T;
24
+ /**
25
+ * The content of the list.
26
+ */
27
+ children?: ReactNode;
28
+ /**
29
+ * Default element type for list items.
30
+ * When render is 'dl', defaults to 'dd'. Otherwise defaults to 'li'.
31
+ */
32
+ defaultItemElement?: ListRootContextValue['defaultItemElement'];
33
+ /**
34
+ * Default className to apply to all list items.
35
+ */
36
+ defaultItemClassName?: string;
37
+ };
38
+
39
+ function ListRootImpl<T extends ListRootElement = 'ul'>(
40
+ props: ListRootProps<T>,
41
+ ref: ForwardedRef<HTMLElement>,
42
+ ) {
43
+ const {
44
+ render = 'ul' as T,
45
+ children,
46
+ defaultItemElement,
47
+ defaultItemClassName,
48
+ ...elementProps
49
+ } = props;
50
+
51
+ const resolvedDefaultItemElement = defaultItemElement ?? (render === 'dl' ? 'dd' : 'li');
52
+
53
+ const contextValue = useMemo<ListRootContextValue>(
54
+ () => ({
55
+ defaultItemClassName,
56
+ defaultItemElement: resolvedDefaultItemElement,
57
+ }),
58
+ [resolvedDefaultItemElement, defaultItemClassName],
59
+ );
60
+
61
+ const Component = render as ElementType;
62
+
63
+ return (
64
+ <ListRootContext.Provider value={contextValue}>
65
+ <Component
66
+ ref={ref}
67
+ {...elementProps}
68
+ >
69
+ {children}
70
+ </Component>
71
+ </ListRootContext.Provider>
72
+ );
73
+ }
74
+
75
+ export const ListRoot = forwardRef(ListRootImpl) as <T extends ListRootElement = 'ul'>(
76
+ props: ListRootProps<T> & { ref?: ForwardedRef<HTMLElement> },
77
+ ) => ReturnType<typeof ListRootImpl>;
78
+
79
+ export namespace ListRoot {
80
+ export type Props<T extends ListRootElement = 'ul'> = ListRootProps<T>;
81
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * CSS custom properties for List component styling.
3
+ * These can be overridden at any level to customize the appearance.
4
+ *
5
+ * Usage with CSS Modules or plain CSS:
6
+ * .my-list {
7
+ * --list-gap: 1rem;
8
+ * --list-padding: 0;
9
+ * }
10
+ *
11
+ * Usage with Tailwind (via arbitrary properties):
12
+ * <List.Root className="[--list-gap:1rem]">
13
+ */
14
+
15
+ :root {
16
+ /* List Root */
17
+ --list-gap: 0;
18
+ --list-padding: 0;
19
+ --list-margin: 0;
20
+ --list-style: none;
21
+
22
+ /* List Item */
23
+ --list-item-padding: 0;
24
+ --list-item-margin: 0;
25
+
26
+ /* Definition List specific */
27
+ --list-dl-grid-columns: auto 1fr;
28
+ --list-dl-gap-x: 0.5rem;
29
+ --list-dl-gap-y: 0.25rem;
30
+ --list-dt-font-weight: 600;
31
+ --list-dd-margin: 0;
32
+ }
@@ -0,0 +1,119 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { List } from './index';
3
+
4
+ const meta: Meta<typeof List.Root> = {
5
+ component: List.Root,
6
+ parameters: {
7
+ layout: 'padded',
8
+ },
9
+ tags: ['autodocs'],
10
+ title: 'Components/List',
11
+ };
12
+
13
+ export default meta;
14
+ type Story = StoryObj<typeof List.Root>;
15
+
16
+ export const UnorderedList: Story = {
17
+ render: () => (
18
+ <List.Root>
19
+ <List.Item>First item</List.Item>
20
+ <List.Item>Second item</List.Item>
21
+ <List.Item>Third item</List.Item>
22
+ </List.Root>
23
+ ),
24
+ };
25
+
26
+ export const OrderedList: Story = {
27
+ render: () => (
28
+ <List.Root render="ol">
29
+ <List.Item>Step one</List.Item>
30
+ <List.Item>Step two</List.Item>
31
+ <List.Item>Step three</List.Item>
32
+ </List.Root>
33
+ ),
34
+ };
35
+
36
+ export const DefinitionList: Story = {
37
+ render: () => (
38
+ <List.Root
39
+ className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-2"
40
+ render="dl"
41
+ >
42
+ <List.Item
43
+ className="font-semibold"
44
+ render="dt"
45
+ >
46
+ Email
47
+ </List.Item>
48
+ <List.Item>contact@example.com</List.Item>
49
+ <List.Item
50
+ className="font-semibold"
51
+ render="dt"
52
+ >
53
+ Phone
54
+ </List.Item>
55
+ <List.Item>+1 234 567 890</List.Item>
56
+ <List.Item
57
+ className="font-semibold"
58
+ render="dt"
59
+ >
60
+ Address
61
+ </List.Item>
62
+ <List.Item>123 Main Street, City, Country</List.Item>
63
+ </List.Root>
64
+ ),
65
+ };
66
+
67
+ export const WithDefaultItemClassName: Story = {
68
+ render: () => (
69
+ <List.Root defaultItemClassName="py-2 border-b border-gray-200 last:border-0">
70
+ <List.Item>Styled item one</List.Item>
71
+ <List.Item>Styled item two</List.Item>
72
+ <List.Item>Styled item three</List.Item>
73
+ </List.Root>
74
+ ),
75
+ };
76
+
77
+ export const InlineList: Story = {
78
+ render: () => (
79
+ <List.Root className="flex flex-wrap gap-2 list-none p-0">
80
+ <List.Item className="px-2 py-1 bg-gray-100 rounded">Tag 1</List.Item>
81
+ <List.Item className="px-2 py-1 bg-gray-100 rounded">Tag 2</List.Item>
82
+ <List.Item className="px-2 py-1 bg-gray-100 rounded">Tag 3</List.Item>
83
+ </List.Root>
84
+ ),
85
+ };
86
+
87
+ export const NavigationMenu: Story = {
88
+ render: () => (
89
+ <List.Root
90
+ className="flex gap-4"
91
+ defaultItemElement="div"
92
+ render="nav"
93
+ >
94
+ <List.Item className="hover:text-blue-600 cursor-pointer">Home</List.Item>
95
+ <List.Item className="hover:text-blue-600 cursor-pointer">About</List.Item>
96
+ <List.Item className="hover:text-blue-600 cursor-pointer">Contact</List.Item>
97
+ </List.Root>
98
+ ),
99
+ };
100
+
101
+ export const CustomElements: Story = {
102
+ render: () => (
103
+ <List.Root
104
+ className="flex gap-2"
105
+ defaultItemElement="span"
106
+ render="div"
107
+ >
108
+ <List.Item className="px-3 py-1 bg-blue-100 text-blue-800 rounded-full text-sm">
109
+ React
110
+ </List.Item>
111
+ <List.Item className="px-3 py-1 bg-green-100 text-green-800 rounded-full text-sm">
112
+ TypeScript
113
+ </List.Item>
114
+ <List.Item className="px-3 py-1 bg-purple-100 text-purple-800 rounded-full text-sm">
115
+ Tailwind
116
+ </List.Item>
117
+ </List.Root>
118
+ ),
119
+ };
@@ -0,0 +1,168 @@
1
+ import { cleanup, render, screen } from '@testing-library/react';
2
+ import { afterEach, describe, expect, it } from 'vitest';
3
+ import { List } from './index';
4
+
5
+ afterEach(() => {
6
+ cleanup();
7
+ });
8
+
9
+ describe('List.Root', () => {
10
+ it('renders as ul by default', () => {
11
+ render(
12
+ <List.Root data-testid="list">
13
+ <List.Item>Item 1</List.Item>
14
+ </List.Root>,
15
+ );
16
+
17
+ const list = screen.getByTestId('list');
18
+ expect(list.tagName).toBe('UL');
19
+ });
20
+
21
+ it('renders as ol when specified', () => {
22
+ render(
23
+ <List.Root
24
+ data-testid="list"
25
+ render="ol"
26
+ >
27
+ <List.Item>Item 1</List.Item>
28
+ </List.Root>,
29
+ );
30
+
31
+ const list = screen.getByTestId('list');
32
+ expect(list.tagName).toBe('OL');
33
+ });
34
+
35
+ it('renders as dl when specified', () => {
36
+ render(
37
+ <List.Root
38
+ data-testid="list"
39
+ render="dl"
40
+ >
41
+ <List.Item render="dt">Term</List.Item>
42
+ <List.Item render="dd">Definition</List.Item>
43
+ </List.Root>,
44
+ );
45
+
46
+ const list = screen.getByTestId('list');
47
+ expect(list.tagName).toBe('DL');
48
+ });
49
+
50
+ it('renders as div when specified', () => {
51
+ render(
52
+ <List.Root
53
+ data-testid="list"
54
+ render="div"
55
+ >
56
+ <List.Item render="div">Item 1</List.Item>
57
+ </List.Root>,
58
+ );
59
+
60
+ const list = screen.getByTestId('list');
61
+ expect(list.tagName).toBe('DIV');
62
+ });
63
+
64
+ it('passes className to root element', () => {
65
+ render(
66
+ <List.Root
67
+ className="custom-class"
68
+ data-testid="list"
69
+ >
70
+ <List.Item>Item 1</List.Item>
71
+ </List.Root>,
72
+ );
73
+
74
+ const list = screen.getByTestId('list');
75
+ expect(list.className).toBe('custom-class');
76
+ });
77
+ });
78
+
79
+ describe('List.Item', () => {
80
+ it('renders as li by default', () => {
81
+ render(
82
+ <List.Root>
83
+ <List.Item data-testid="item">Item 1</List.Item>
84
+ </List.Root>,
85
+ );
86
+
87
+ const item = screen.getByTestId('item');
88
+ expect(item.tagName).toBe('LI');
89
+ });
90
+
91
+ it('renders as dd when parent is dl', () => {
92
+ render(
93
+ <List.Root render="dl">
94
+ <List.Item data-testid="item">Definition</List.Item>
95
+ </List.Root>,
96
+ );
97
+
98
+ const item = screen.getByTestId('item');
99
+ expect(item.tagName).toBe('DD');
100
+ });
101
+
102
+ it('allows explicit render override', () => {
103
+ render(
104
+ <List.Root render="dl">
105
+ <List.Item
106
+ data-testid="item"
107
+ render="dt"
108
+ >
109
+ Term
110
+ </List.Item>
111
+ </List.Root>,
112
+ );
113
+
114
+ const item = screen.getByTestId('item');
115
+ expect(item.tagName).toBe('DT');
116
+ });
117
+
118
+ it('applies defaultItemClassName from context', () => {
119
+ render(
120
+ <List.Root defaultItemClassName="default-item-class">
121
+ <List.Item data-testid="item">Item 1</List.Item>
122
+ </List.Root>,
123
+ );
124
+
125
+ const item = screen.getByTestId('item');
126
+ expect(item.className).toBe('default-item-class');
127
+ });
128
+
129
+ it('merges defaultItemClassName with item className', () => {
130
+ render(
131
+ <List.Root defaultItemClassName="default-class">
132
+ <List.Item
133
+ className="custom-class"
134
+ data-testid="item"
135
+ >
136
+ Item 1
137
+ </List.Item>
138
+ </List.Root>,
139
+ );
140
+
141
+ const item = screen.getByTestId('item');
142
+ expect(item.className).toBe('default-class custom-class');
143
+ });
144
+
145
+ it('passes className when no defaultItemClassName', () => {
146
+ render(
147
+ <List.Root>
148
+ <List.Item
149
+ className="custom-class"
150
+ data-testid="item"
151
+ >
152
+ Item 1
153
+ </List.Item>
154
+ </List.Root>,
155
+ );
156
+
157
+ const item = screen.getByTestId('item');
158
+ expect(item.className).toBe('custom-class');
159
+ });
160
+
161
+ it('works without List.Root context', () => {
162
+ render(<List.Item data-testid="item">Standalone Item</List.Item>);
163
+
164
+ const item = screen.getByTestId('item');
165
+ expect(item.tagName).toBe('LI');
166
+ expect(item.textContent).toBe('Standalone Item');
167
+ });
168
+ });
@@ -0,0 +1,2 @@
1
+ export type { MarkdownContainerProps, MarkdownOverrides, MDXComponent } from './markdown-container';
2
+ export { MarkdownContainer, transformLink } from './markdown-container';
@@ -1,5 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { MarkdownContainer } from '../components/markdown-container';
2
+ import { MarkdownContainer } from './markdown-container';
3
3
 
4
4
  const meta: Meta<typeof MarkdownContainer> = {
5
5
  component: MarkdownContainer,
@@ -74,3 +74,58 @@ export const WithCharacterLimit: Story = {
74
74
  locale: 'en',
75
75
  },
76
76
  };
77
+
78
+ export const WithMDXComponents: Story = {
79
+ args: {
80
+ children: `# Custom Components
81
+
82
+ This text uses custom MDX components.`,
83
+ locale: 'en',
84
+ mdxComponents: {},
85
+ },
86
+ };
87
+
88
+ export const WithMarkdownOverrides: Story = {
89
+ args: {
90
+ children: `# Overridden Heading
91
+
92
+ This paragraph has custom styling applied.`,
93
+ locale: 'en',
94
+ markdownOverrides: {
95
+ h1: {
96
+ props: {
97
+ className: 'text-4xl font-bold text-blue-600',
98
+ },
99
+ },
100
+ },
101
+ },
102
+ };
103
+
104
+ export const WithInternalLink: Story = {
105
+ args: {
106
+ children: 'Check out [our page](https://regardio.com/about) for more info.',
107
+ locale: 'en',
108
+ },
109
+ };
110
+
111
+ export const WithExternalLink: Story = {
112
+ args: {
113
+ children: 'Visit [external site](https://example.com) for details.',
114
+ locale: 'en',
115
+ },
116
+ };
117
+
118
+ export const EmptyContent: Story = {
119
+ args: {
120
+ children: '',
121
+ locale: 'en',
122
+ },
123
+ };
124
+
125
+ export const WithClassName: Story = {
126
+ args: {
127
+ children: '**Bold text** in a styled container.',
128
+ className: 'bg-gray-100 p-4 rounded',
129
+ locale: 'en',
130
+ },
131
+ };
@@ -1,7 +1,9 @@
1
+ 'use client';
2
+
1
3
  import { MDXProvider } from '@mdx-js/react';
4
+ import { cn } from '@regardio/tailwind/utils';
2
5
  import Markdown, { type MarkdownToJSX } from 'markdown-to-jsx';
3
6
  import type React from 'react';
4
- import { cn } from '../utils/cn';
5
7
  import { replaceSpecialChars } from '../utils/text';
6
8
 
7
9
  const doubleNewlineRegex = /\n\n+/;
@@ -0,0 +1,2 @@
1
+ export type { InputProps, PasswordInputProps } from './password-input';
2
+ export { PasswordInput } from './password-input';
@@ -1,5 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { PasswordInput } from '../components/password-input';
2
+ import { PasswordInput } from './password-input';
3
3
 
4
4
  const meta: Meta<typeof PasswordInput> = {
5
5
  component: PasswordInput,
@@ -1,7 +1,9 @@
1
+ 'use client';
2
+
1
3
  import { Button } from '@base-ui/react/button';
2
4
  import { Input } from '@base-ui/react/input';
5
+ import { cn } from '@regardio/tailwind/utils';
3
6
  import { useState } from 'react';
4
- import { cn } from '../utils/cn';
5
7
 
6
8
  export interface InputProps extends React.ComponentPropsWithoutRef<typeof Input> {}
7
9
 
@@ -9,7 +11,7 @@ export interface PasswordInputProps extends InputProps {
9
11
  className?: string;
10
12
  }
11
13
 
12
- const PasswordInput = ({ className, ...props }: PasswordInputProps) => {
14
+ export const PasswordInput = ({ className, ...props }: PasswordInputProps) => {
13
15
  const [showPassword, setShowPassword] = useState(false);
14
16
 
15
17
  const togglePasswordVisibility = () => {
@@ -45,5 +47,3 @@ const PasswordInput = ({ className, ...props }: PasswordInputProps) => {
45
47
  </div>
46
48
  );
47
49
  };
48
-
49
- export { PasswordInput };
@@ -0,0 +1,2 @@
1
+ export type { ImageFormat, PictureProps } from './picture';
2
+ export { Picture, screenSizeVariants } from './picture';
@@ -1,5 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { Picture } from '../components/picture';
2
+ import { Picture } from './picture';
3
3
 
4
4
  const meta: Meta<typeof Picture> = {
5
5
  component: Picture,
@@ -1,5 +1,5 @@
1
+ import { cn } from '@regardio/tailwind/utils';
1
2
  import type React from 'react';
2
- import { cn } from '../utils/cn';
3
3
 
4
4
  // Define screen size variants for responsive images
5
5
  // Define screen size variants for responsive images (used in srcset generation)
@@ -40,7 +40,7 @@ export interface PictureProps {
40
40
  * @param placeholder - Placeholder to replace in baseUrl (default: '{format}')
41
41
  * @param sizes - Custom sizes attribute
42
42
  */
43
- const Picture: React.FC<PictureProps> = ({
43
+ export const Picture: React.FC<PictureProps> = ({
44
44
  alt,
45
45
  baseUrl,
46
46
  className,
@@ -103,5 +103,3 @@ const Picture: React.FC<PictureProps> = ({
103
103
  </picture>
104
104
  );
105
105
  };
106
-
107
- export { Picture };
@@ -0,0 +1,2 @@
1
+ export type { ProtectedEmailProps } from './protected-email';
2
+ export { ProtectedEmail } from './protected-email';
@@ -1,5 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { ProtectedEmail } from '../components/protected-email';
2
+ import { ProtectedEmail } from './protected-email';
3
3
 
4
4
  const meta: Meta<typeof ProtectedEmail> = {
5
5
  component: ProtectedEmail,
@@ -1,6 +1,8 @@
1
+ 'use client';
2
+
1
3
  import { useEffect, useState } from 'react';
2
4
 
3
- interface ProtectedEmailProps {
5
+ export interface ProtectedEmailProps {
4
6
  username: string;
5
7
  domain: string;
6
8
  text?: string;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Tailwind CSS Source Scanning for @regardio/react
3
+ *
4
+ * This file enables Tailwind to detect utility classes used in @regardio/react components.
5
+ * Import this in your app's main CSS file to ensure component styles are included:
6
+ *
7
+ * @import "@regardio/react/tailwind.css";
8
+ */
9
+
10
+ @source "../";
@@ -0,0 +1,2 @@
1
+ export type { TextProps } from './text';
2
+ export { Text } from './text';
@@ -1,5 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { Text } from '../components/text';
2
+ import { Text } from './text';
3
3
 
4
4
  const meta: Meta<typeof Text> = {
5
5
  component: Text,
@@ -0,0 +1,46 @@
1
+ import { tv } from '@regardio/tailwind/utils';
2
+ import type { ComponentProps } from 'react';
3
+
4
+ const themeColorVariants = {
5
+ primary: [],
6
+ } as const;
7
+
8
+ const textVariants = {
9
+ code: ['font-light', 'font-monospace'],
10
+ primary: [],
11
+ subtitle: ['text-lg'],
12
+ } as const;
13
+
14
+ const text = tv({
15
+ base: ['relative', 'block'],
16
+ defaultVariants: {
17
+ themeColor: 'primary',
18
+ variant: 'primary',
19
+ },
20
+ variants: {
21
+ themeColor: themeColorVariants,
22
+ variant: textVariants,
23
+ },
24
+ });
25
+
26
+ export type TextThemeColor = keyof typeof themeColorVariants;
27
+ export type TextVariant = keyof typeof textVariants;
28
+
29
+ export interface TextProps extends ComponentProps<'p'> {
30
+ themeColor?: TextThemeColor;
31
+ variant?: TextVariant;
32
+ }
33
+
34
+ export const Text = ({ children, className, variant, themeColor }: TextProps) => {
35
+ return (
36
+ <div
37
+ className={text({
38
+ className,
39
+ themeColor,
40
+ variant,
41
+ })}
42
+ >
43
+ {children}
44
+ </div>
45
+ );
46
+ };