@pyreon/elements 0.11.1 → 0.11.2

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 (52) hide show
  1. package/package.json +8 -7
  2. package/src/Element/component.tsx +211 -0
  3. package/src/Element/constants.ts +96 -0
  4. package/src/Element/index.ts +6 -0
  5. package/src/Element/types.ts +168 -0
  6. package/src/Element/utils.ts +15 -0
  7. package/src/List/component.tsx +57 -0
  8. package/src/List/index.ts +5 -0
  9. package/src/Overlay/component.tsx +131 -0
  10. package/src/Overlay/context.tsx +37 -0
  11. package/src/Overlay/index.ts +7 -0
  12. package/src/Overlay/useOverlay.tsx +616 -0
  13. package/src/Portal/component.tsx +41 -0
  14. package/src/Portal/index.ts +5 -0
  15. package/src/Text/component.tsx +65 -0
  16. package/src/Text/index.ts +5 -0
  17. package/src/Text/styled.ts +30 -0
  18. package/src/Util/component.tsx +43 -0
  19. package/src/Util/index.ts +5 -0
  20. package/src/__tests__/Content.test.tsx +115 -0
  21. package/src/__tests__/Element.test.ts +604 -0
  22. package/src/__tests__/Iterator.test.ts +483 -0
  23. package/src/__tests__/List.test.ts +199 -0
  24. package/src/__tests__/Overlay.test.ts +485 -0
  25. package/src/__tests__/Portal.test.ts +82 -0
  26. package/src/__tests__/Text.test.ts +274 -0
  27. package/src/__tests__/Util.test.ts +63 -0
  28. package/src/__tests__/Wrapper.test.tsx +152 -0
  29. package/src/__tests__/equalBeforeAfter.test.ts +122 -0
  30. package/src/__tests__/helpers.test.ts +65 -0
  31. package/src/__tests__/overlayContext.test.tsx +78 -0
  32. package/src/__tests__/responsiveProps.test.ts +298 -0
  33. package/src/__tests__/useOverlay.test.ts +1330 -0
  34. package/src/__tests__/utils.test.ts +69 -0
  35. package/src/constants.ts +1 -0
  36. package/src/helpers/Content/component.tsx +51 -0
  37. package/src/helpers/Content/index.ts +3 -0
  38. package/src/helpers/Content/styled.ts +105 -0
  39. package/src/helpers/Content/types.ts +49 -0
  40. package/src/helpers/Iterator/component.tsx +252 -0
  41. package/src/helpers/Iterator/index.ts +13 -0
  42. package/src/helpers/Iterator/types.ts +79 -0
  43. package/src/helpers/Wrapper/component.tsx +78 -0
  44. package/src/helpers/Wrapper/constants.ts +10 -0
  45. package/src/helpers/Wrapper/index.ts +3 -0
  46. package/src/helpers/Wrapper/styled.ts +69 -0
  47. package/src/helpers/Wrapper/types.ts +56 -0
  48. package/src/helpers/Wrapper/utils.ts +7 -0
  49. package/src/helpers/index.ts +4 -0
  50. package/src/index.ts +37 -0
  51. package/src/types.ts +81 -0
  52. package/src/utils.ts +1 -0
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Text component for rendering inline or block-level text. Supports a
3
+ * `paragraph` shorthand that automatically renders as a `<p>` tag, or
4
+ * a custom `tag` for semantic HTML (h1-h6, span, etc.). Marked with
5
+ * a static `isText` flag so other components can detect text children.
6
+ */
7
+
8
+ import type { PyreonHTMLAttributes, VNodeChild } from "@pyreon/core"
9
+ import type { HTMLTextTags } from "@pyreon/ui-core"
10
+ import { PKG_NAME } from "../constants"
11
+ import type { ExtendCss, PyreonComponent } from "../types"
12
+ import Styled from "./styled"
13
+
14
+ export type Props = Partial<{
15
+ /**
16
+ * Label can be used instead of children for inline syntax. But **children** prop takes a precedence
17
+ */
18
+ label: VNodeChild
19
+ /**
20
+ * Children to be rendered within **Text** component.
21
+ */
22
+ children: VNodeChild
23
+ /**
24
+ * Defines whether should behave as a block text element. Automatically adds **p** HTML tag
25
+ */
26
+ paragraph: boolean
27
+ /**
28
+ * Defines what kind of HTML tag should be rendered
29
+ */
30
+ tag: HTMLTextTags
31
+ /**
32
+ * If an additional styling needs to be added, it can be do so via injecting styles using this property.
33
+ */
34
+ css: ExtendCss
35
+ }> &
36
+ PyreonHTMLAttributes
37
+
38
+ const Component: PyreonComponent<Props> & {
39
+ isText?: true
40
+ } = ({ paragraph, label, children, tag, css, ref, ...props }) => {
41
+ let finalTag: string | undefined
42
+
43
+ if (paragraph) finalTag = "p"
44
+ else {
45
+ finalTag = tag
46
+ }
47
+
48
+ return (
49
+ <Styled ref={ref} as={finalTag} $text={{ extraStyles: css }} {...props}>
50
+ {children ?? label}
51
+ </Styled>
52
+ )
53
+ }
54
+
55
+ // ----------------------------------------------
56
+ // DEFINE STATICS
57
+ // ----------------------------------------------
58
+ const name = `${PKG_NAME}/Text` as const
59
+
60
+ Component.displayName = name
61
+ Component.pkgName = PKG_NAME
62
+ Component.PYREON__COMPONENT = name
63
+ Component.isText = true
64
+
65
+ export default Component
@@ -0,0 +1,5 @@
1
+ import type { Props } from "./component"
2
+ import component from "./component"
3
+
4
+ export type { Props as TextProps }
5
+ export { component as Text }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Styled text primitive that inherits color, font-weight, and line-height
3
+ * from its parent so it blends seamlessly into any context. Additional
4
+ * styles can be injected via the responsive `extraStyles` prop processed
5
+ * through makeItResponsive.
6
+ */
7
+ import { config } from "@pyreon/ui-core"
8
+ import { extendCss, makeItResponsive } from "@pyreon/unistyle"
9
+ import type { ResponsiveStylesCallback } from "../types"
10
+
11
+ const { styled, css, textComponent } = config
12
+
13
+ const styles: ResponsiveStylesCallback = ({ css: cssFn, theme: t }) => cssFn`
14
+ ${t.extraStyles && extendCss(t.extraStyles)};
15
+ `
16
+
17
+ export default styled(textComponent)`
18
+ ${css`
19
+ color: inherit;
20
+ font-weight: inherit;
21
+ line-height: 1;
22
+ `};
23
+
24
+ ${makeItResponsive({
25
+ key: "$text",
26
+ styles,
27
+ css,
28
+ normalize: false,
29
+ })};
30
+ `
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Utility wrapper that injects className and/or style props into its
3
+ * children without adding any DOM nodes of its own. Uses the core `render`
4
+ * helper to clone children with the merged props.
5
+ */
6
+
7
+ import type { VNode, VNodeChild } from "@pyreon/core"
8
+ import { render } from "@pyreon/ui-core"
9
+ import { PKG_NAME } from "../constants"
10
+ import type { PyreonComponent } from "../types"
11
+
12
+ export interface Props {
13
+ /**
14
+ * Children to be rendered within **Util** component.
15
+ */
16
+ children: VNodeChild
17
+ /**
18
+ * Class name(s) to be added to children component.
19
+ */
20
+ className?: string | string[] | undefined
21
+ /**
22
+ * Style property to extend children component inline styles
23
+ */
24
+ style?: Record<string, unknown> | undefined
25
+ }
26
+
27
+ const Component: PyreonComponent<Props> = (({ children, className, style }: Props) => {
28
+ const mergedClasses = Array.isArray(className) ? className.join(" ") : className
29
+
30
+ const finalProps: Record<string, any> = {}
31
+ if (style) finalProps.style = style
32
+ if (mergedClasses) finalProps.className = mergedClasses
33
+
34
+ return render(children, finalProps) as VNode | null
35
+ }) as PyreonComponent<Props>
36
+
37
+ const name = `${PKG_NAME}/Util` as const
38
+
39
+ Component.displayName = name
40
+ Component.pkgName = PKG_NAME
41
+ Component.PYREON__COMPONENT = name
42
+
43
+ export default Component
@@ -0,0 +1,5 @@
1
+ import type { Props } from "./component"
2
+ import component from "./component"
3
+
4
+ export type { Props as UtilProps }
5
+ export { component as Util }
@@ -0,0 +1,115 @@
1
+ import type { VNode } from "@pyreon/core"
2
+ import { beforeEach, describe, expect, it, vi } from "vitest"
3
+
4
+ // ---------------------------------------------------------------------------
5
+ // Mocks
6
+ // ---------------------------------------------------------------------------
7
+ const mocks = vi.hoisted(() => ({
8
+ render: vi.fn((children: unknown) => children),
9
+ }))
10
+
11
+ vi.mock("@pyreon/ui-core", async (importOriginal) => {
12
+ const actual = (await importOriginal()) as Record<string, unknown>
13
+ return {
14
+ ...actual,
15
+ render: mocks.render,
16
+ }
17
+ })
18
+
19
+ vi.mock("~/utils", () => ({
20
+ IS_DEVELOPMENT: true,
21
+ }))
22
+
23
+ import Content from "../helpers/Content/component"
24
+ import Styled from "../helpers/Content/styled"
25
+
26
+ const asVNode = (v: unknown) => v as VNode
27
+
28
+ describe("Content component", () => {
29
+ beforeEach(() => {
30
+ vi.clearAllMocks()
31
+ })
32
+
33
+ it("returns a VNode whose type is the Styled component", () => {
34
+ const result = asVNode(Content({ contentType: "content" }))
35
+ expect(result.type).toBe(Styled)
36
+ })
37
+
38
+ it("passes tag as the 'as' prop to Styled", () => {
39
+ const result = asVNode(Content({ tag: "span" }))
40
+ expect(result.props.as).toBe("span")
41
+ })
42
+
43
+ it("passes contentType as $contentType prop", () => {
44
+ const result = asVNode(Content({ contentType: "before" }))
45
+ expect(result.props.$contentType).toBe("before")
46
+ })
47
+
48
+ it("passes styling props bundled as $element prop", () => {
49
+ const result = asVNode(
50
+ Content({
51
+ contentType: "content",
52
+ parentDirection: "inline",
53
+ direction: "rows",
54
+ alignX: "center",
55
+ alignY: "top",
56
+ equalCols: true,
57
+ gap: 8,
58
+ extendCss: "color: red;",
59
+ }),
60
+ )
61
+
62
+ expect(result.props.$element).toEqual({
63
+ contentType: "content",
64
+ parentDirection: "inline",
65
+ direction: "rows",
66
+ alignX: "center",
67
+ alignY: "top",
68
+ equalCols: true,
69
+ gap: 8,
70
+ extraStyles: "color: red;",
71
+ })
72
+ })
73
+
74
+ it("adds data-pyr-element attribute in development mode", () => {
75
+ const result = asVNode(Content({ contentType: "after" }))
76
+ expect(result.props["data-pyr-element"]).toBe("after")
77
+ })
78
+
79
+ it("passes children through render()", () => {
80
+ const children = "Some text"
81
+ Content({ children })
82
+ expect(mocks.render).toHaveBeenCalledWith(children)
83
+ })
84
+
85
+ it("spreads remaining props to Styled", () => {
86
+ const result = asVNode(Content({ id: "test-id", className: "custom" } as any))
87
+ expect(result.props.id).toBe("test-id")
88
+ expect(result.props.className).toBe("custom")
89
+ })
90
+
91
+ it("maps extendCss to extraStyles in $element", () => {
92
+ const result = asVNode(Content({ extendCss: "font-size: 14px;" }))
93
+ expect((result.props.$element as any).extraStyles).toBe("font-size: 14px;")
94
+ })
95
+ })
96
+
97
+ describe("Content component (production mode)", () => {
98
+ it("does not add data-pyr-element when IS_DEVELOPMENT is false", async () => {
99
+ // Reset module registry so the new mock takes effect
100
+ vi.resetModules()
101
+ vi.doMock("../utils", () => ({
102
+ IS_DEVELOPMENT: false,
103
+ }))
104
+ // Re-mock @pyreon/ui-core after resetModules
105
+ vi.doMock("@pyreon/ui-core", async (importOriginal) => {
106
+ const actual = (await importOriginal()) as Record<string, unknown>
107
+ return { ...actual, render: (children: unknown) => children }
108
+ })
109
+
110
+ const { default: ContentProd } = await import("../helpers/Content/component")
111
+ const result = asVNode(ContentProd({ contentType: "before" }))
112
+
113
+ expect(result.props["data-pyr-element"]).toBeUndefined()
114
+ })
115
+ })