@tallyui/components 1.0.0 → 2.0.0

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 (136) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.d.ts +1076 -76
  3. package/dist/index.js +4662 -355
  4. package/package.json +27 -5
  5. package/src/cart/cart-line-actions.tsx +40 -0
  6. package/src/cart/cart-line.tsx +18 -32
  7. package/src/cart/cart-note-input.tsx +46 -0
  8. package/src/cart/cart-panel.tsx +39 -26
  9. package/src/cart/cart-total.tsx +43 -47
  10. package/src/cart/discount-badge.tsx +19 -0
  11. package/src/cart/index.ts +4 -1
  12. package/src/checkout/cash-tendered.tsx +101 -0
  13. package/src/checkout/change-display.tsx +19 -0
  14. package/src/checkout/index.ts +3 -0
  15. package/src/checkout/order-summary-line.tsx +7 -8
  16. package/src/checkout/order-summary.tsx +8 -7
  17. package/src/checkout/payment-method-card.tsx +10 -9
  18. package/src/checkout/payment-selector.tsx +5 -5
  19. package/src/checkout/receipt-preview.tsx +78 -0
  20. package/src/customer/customer-card.tsx +8 -9
  21. package/src/customer/customer-form.tsx +70 -0
  22. package/src/customer/customer-order-history.tsx +34 -0
  23. package/src/customer/customer-select.tsx +63 -24
  24. package/src/customer/index.ts +2 -0
  25. package/src/index.ts +112 -1
  26. package/src/input/filter-chip.tsx +85 -27
  27. package/src/input/index.ts +1 -0
  28. package/src/input/quantity-stepper.tsx +64 -0
  29. package/src/input/search-input.tsx +30 -7
  30. package/src/layout/checkout-layout.tsx +46 -0
  31. package/src/layout/index.ts +5 -0
  32. package/src/layout/live-tab-screen.tsx +54 -0
  33. package/src/layout/pos-layout.tsx +64 -0
  34. package/src/layout/settings-layout.tsx +58 -0
  35. package/src/layout/store-settings-choice-screen.tsx +142 -0
  36. package/src/order/index.ts +4 -0
  37. package/src/order/order-card.tsx +48 -0
  38. package/src/order/order-detail.tsx +82 -0
  39. package/src/order/order-list.tsx +39 -0
  40. package/src/order/order-status-badge.tsx +32 -0
  41. package/src/product/category-nav.tsx +70 -0
  42. package/src/product/index.ts +3 -0
  43. package/src/product/product-card.tsx +36 -8
  44. package/src/product/product-grid.tsx +25 -6
  45. package/src/product/product-image.tsx +28 -7
  46. package/src/product/product-list.tsx +44 -0
  47. package/src/product/product-price.tsx +77 -22
  48. package/src/product/product-sku.tsx +5 -5
  49. package/src/product/product-stock-badge.tsx +41 -28
  50. package/src/product/product-title.tsx +2 -3
  51. package/src/product/product-variant-picker.tsx +61 -0
  52. package/src/register/cash-count-input.tsx +85 -0
  53. package/src/register/closure-sheet.tsx +155 -0
  54. package/src/register/index.ts +12 -0
  55. package/src/register/movement-sheet.tsx +118 -0
  56. package/src/register/open-register-card.tsx +109 -0
  57. package/src/register/register-bar.helpers.ts +57 -0
  58. package/src/register/register-bar.tsx +85 -0
  59. package/src/register/register-column.tsx +117 -0
  60. package/src/register/register-count.tsx +330 -0
  61. package/src/register/register-open-close.tsx +52 -0
  62. package/src/register/register-panel.tsx +151 -0
  63. package/src/register/register-picker.tsx +60 -0
  64. package/src/register/register-summary.tsx +72 -0
  65. package/src/sale/cart-bar.tsx +18 -0
  66. package/src/sale/cart.tsx +66 -0
  67. package/src/sale/catalogue.tsx +131 -0
  68. package/src/sale/discount-form.tsx +85 -0
  69. package/src/sale/index.ts +9 -0
  70. package/src/sale/orders-list.tsx +66 -0
  71. package/src/sale/print-style.ts +11 -0
  72. package/src/sale/receipt.tsx +61 -0
  73. package/src/sale/sync-status.tsx +20 -0
  74. package/src/sale/tender.tsx +35 -0
  75. package/src/settings/connector-status.tsx +65 -0
  76. package/src/settings/index.ts +3 -0
  77. package/src/settings/settings-group.tsx +23 -0
  78. package/src/settings/settings-row.tsx +23 -0
  79. package/src/ui/accordion/index.tsx +59 -0
  80. package/src/ui/alert-dialog/index.tsx +91 -0
  81. package/src/ui/avatar/index.tsx +50 -0
  82. package/src/ui/badge/index.tsx +60 -0
  83. package/src/ui/button/index.tsx +90 -0
  84. package/src/ui/card/index.tsx +60 -0
  85. package/src/ui/checkbox/index.tsx +29 -0
  86. package/src/ui/collapsible/index.tsx +24 -0
  87. package/src/ui/combobox/index.tsx +98 -0
  88. package/src/ui/context-menu/index.tsx +159 -0
  89. package/src/ui/dialog/index.tsx +92 -0
  90. package/src/ui/dropdown-menu/index.tsx +161 -0
  91. package/src/ui/hover-card/index.tsx +31 -0
  92. package/src/ui/hstack/index.tsx +43 -0
  93. package/src/ui/icon/index.tsx +44 -0
  94. package/src/ui/icon/search-icon.tsx +9 -0
  95. package/src/ui/icon/svg-icon.tsx +36 -0
  96. package/src/ui/icon-button/index.tsx +58 -0
  97. package/src/ui/index.ts +99 -0
  98. package/src/ui/input/index.tsx +100 -0
  99. package/src/ui/label/index.tsx +26 -0
  100. package/src/ui/loader/index.tsx +37 -0
  101. package/src/ui/popover/index.tsx +31 -0
  102. package/src/ui/progress/index.tsx +28 -0
  103. package/src/ui/radio-group/index.tsx +38 -0
  104. package/src/ui/select/index.tsx +96 -0
  105. package/src/ui/separator/index.tsx +29 -0
  106. package/src/ui/slider/index.tsx +28 -0
  107. package/src/ui/switch/index.tsx +31 -0
  108. package/src/ui/table/index.tsx +59 -0
  109. package/src/ui/tabs/index.tsx +53 -0
  110. package/src/ui/text/index.tsx +36 -0
  111. package/src/ui/textarea/index.tsx +29 -0
  112. package/src/ui/toast/index.tsx +52 -0
  113. package/src/ui/toggle/index.tsx +69 -0
  114. package/src/ui/toggle-group/index.tsx +66 -0
  115. package/src/ui/tooltip/index.tsx +30 -0
  116. package/src/ui/vstack/index.tsx +43 -0
  117. package/src/uniwind-env.d.ts +9 -1
  118. package/src/__tests__/cart-line.test.tsx +0 -53
  119. package/src/__tests__/cart-panel.test.tsx +0 -55
  120. package/src/__tests__/cart-total.test.tsx +0 -60
  121. package/src/__tests__/customer-card.test.tsx +0 -47
  122. package/src/__tests__/customer-select.test.tsx +0 -33
  123. package/src/__tests__/filter-chip.test.tsx +0 -40
  124. package/src/__tests__/helpers.ts +0 -111
  125. package/src/__tests__/order-summary-line.test.tsx +0 -18
  126. package/src/__tests__/order-summary.test.tsx +0 -32
  127. package/src/__tests__/payment-method-card.test.tsx +0 -24
  128. package/src/__tests__/payment-selector.test.tsx +0 -23
  129. package/src/__tests__/product-card.test.tsx +0 -31
  130. package/src/__tests__/product-grid.test.tsx +0 -37
  131. package/src/__tests__/product-image.test.tsx +0 -54
  132. package/src/__tests__/product-price.test.tsx +0 -68
  133. package/src/__tests__/product-sku.test.tsx +0 -47
  134. package/src/__tests__/product-stock-badge.test.tsx +0 -58
  135. package/src/__tests__/product-title.test.tsx +0 -38
  136. package/src/__tests__/search-input.test.tsx +0 -22
@@ -0,0 +1,36 @@
1
+ import * as React from 'react';
2
+ import { Text as RNText } from 'react-native';
3
+ import { Slot, TextClassContext } from '@tallyui/primitives';
4
+ import { cn } from '@tallyui/theme';
5
+ import { cva, type VariantProps } from 'class-variance-authority';
6
+
7
+ const textVariants = cva('text-foreground web:select-text text-base', {
8
+ variants: {
9
+ variant: {
10
+ default: '',
11
+ link: 'web:hover:underline web:focus:underline web:hover:cursor-pointer group-active:underline',
12
+ },
13
+ },
14
+ defaultVariants: {
15
+ variant: 'default',
16
+ },
17
+ });
18
+
19
+ type TextProps = React.ComponentPropsWithoutRef<typeof RNText> &
20
+ VariantProps<typeof textVariants> & {
21
+ asChild?: boolean;
22
+ };
23
+
24
+ function Text({ className, variant, asChild = false, children, ...props }: TextProps) {
25
+ const textClass = React.useContext(TextClassContext);
26
+ const Component = asChild ? Slot : RNText;
27
+
28
+ return (
29
+ <Component className={cn(textVariants({ variant }), textClass, className)} {...props}>
30
+ {children}
31
+ </Component>
32
+ );
33
+ }
34
+
35
+ export { Text, TextClassContext, textVariants };
36
+ export type { TextProps };
@@ -0,0 +1,29 @@
1
+ import * as React from 'react';
2
+ import { TextInput, type TextInputProps } from 'react-native';
3
+ import { cn } from '@tallyui/theme';
4
+
5
+ type TextareaProps = TextInputProps & {
6
+ className?: string;
7
+ };
8
+
9
+ const Textarea = React.forwardRef<TextInput, TextareaProps>(
10
+ function Textarea({ className, ...props }, ref) {
11
+ return (
12
+ <TextInput
13
+ ref={ref}
14
+ multiline
15
+ textAlignVertical="top"
16
+ className={cn(
17
+ 'min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground web:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2',
18
+ className,
19
+ )}
20
+ {...props}
21
+ />
22
+ );
23
+ }
24
+ );
25
+
26
+ Textarea.displayName = 'Textarea';
27
+
28
+ export { Textarea };
29
+ export type { TextareaProps };
@@ -0,0 +1,52 @@
1
+ import * as React from 'react';
2
+ import { View, Text, Pressable } from 'react-native';
3
+ import { Toast as ToastPrimitive } from '@tallyui/primitives';
4
+ import { cn } from '@tallyui/theme';
5
+ import { cva, type VariantProps } from 'class-variance-authority';
6
+
7
+ const toastVariants = cva(
8
+ 'group pointer-events-auto relative flex w-full flex-row items-center justify-between gap-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg',
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: 'border-border bg-background text-foreground',
13
+ destructive: 'border-destructive bg-destructive text-destructive-foreground',
14
+ success: 'border-success bg-success text-success-foreground',
15
+ },
16
+ },
17
+ defaultVariants: {
18
+ variant: 'default',
19
+ },
20
+ }
21
+ );
22
+
23
+ type ToastProps = React.ComponentPropsWithoutRef<typeof View> & VariantProps<typeof toastVariants>;
24
+
25
+ function Toast({ className, variant, ...props }: ToastProps) {
26
+ return <View className={cn(toastVariants({ variant }), className)} {...props} />;
27
+ }
28
+
29
+ function ToastTitle({ className, ...props }: React.ComponentPropsWithoutRef<typeof Text>) {
30
+ return <Text className={cn('text-sm font-semibold', className)} {...props} />;
31
+ }
32
+
33
+ function ToastDescription({ className, ...props }: React.ComponentPropsWithoutRef<typeof Text>) {
34
+ return <Text className={cn('text-sm opacity-90', className)} {...props} />;
35
+ }
36
+
37
+ function ToastClose({ className, ...props }: React.ComponentPropsWithoutRef<typeof Pressable>) {
38
+ return (
39
+ <Pressable
40
+ className={cn(
41
+ 'absolute right-2 top-2 rounded-md p-1 opacity-0 web:transition-opacity web:hover:opacity-100 web:focus:opacity-100 web:group-hover:opacity-100',
42
+ className,
43
+ )}
44
+ {...props}
45
+ >
46
+ <Text className="text-sm">✕</Text>
47
+ </Pressable>
48
+ );
49
+ }
50
+
51
+ export { Toast, ToastTitle, ToastDescription, ToastClose, toastVariants };
52
+ export type { ToastProps };
@@ -0,0 +1,69 @@
1
+ import * as React from 'react';
2
+ import { Toggle as TogglePrimitive, TextClassContext } from '@tallyui/primitives';
3
+ import { cn } from '@tallyui/theme';
4
+ import { cva, type VariantProps } from 'class-variance-authority';
5
+
6
+ const toggleVariants = cva(
7
+ 'inline-flex items-center justify-center rounded-md text-sm font-medium web:ring-offset-background web:transition-colors web:hover:bg-muted web:hover:text-muted-foreground web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2',
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: 'bg-transparent',
12
+ outline: 'border border-input bg-transparent web:hover:bg-accent web:hover:text-accent-foreground',
13
+ },
14
+ size: {
15
+ default: 'h-10 px-3',
16
+ sm: 'h-9 px-2.5',
17
+ lg: 'h-11 px-5',
18
+ },
19
+ },
20
+ defaultVariants: {
21
+ variant: 'default',
22
+ size: 'default',
23
+ },
24
+ }
25
+ );
26
+
27
+ const toggleTextVariants = cva('text-sm font-medium', {
28
+ variants: {
29
+ variant: {
30
+ default: '',
31
+ outline: '',
32
+ },
33
+ size: {
34
+ default: '',
35
+ sm: '',
36
+ lg: '',
37
+ },
38
+ },
39
+ defaultVariants: {
40
+ variant: 'default',
41
+ size: 'default',
42
+ },
43
+ });
44
+
45
+ type ToggleProps = React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &
46
+ VariantProps<typeof toggleVariants>;
47
+
48
+ const Toggle = React.forwardRef<
49
+ React.ComponentRef<typeof TogglePrimitive.Root>,
50
+ ToggleProps
51
+ >(function Toggle({ className, variant, size, ...props }, ref) {
52
+ return (
53
+ <TextClassContext.Provider value={toggleTextVariants({ variant, size })}>
54
+ <TogglePrimitive.Root
55
+ ref={ref}
56
+ className={cn(
57
+ toggleVariants({ variant, size }),
58
+ props.pressed && 'bg-accent text-accent-foreground',
59
+ className,
60
+ )}
61
+ {...props}
62
+ />
63
+ </TextClassContext.Provider>
64
+ );
65
+ });
66
+ Toggle.displayName = 'Toggle';
67
+
68
+ export { Toggle, toggleVariants, toggleTextVariants };
69
+ export type { ToggleProps };
@@ -0,0 +1,66 @@
1
+ import * as React from 'react';
2
+ import { ToggleGroup as ToggleGroupPrimitive, TextClassContext } from '@tallyui/primitives';
3
+ import { cn } from '@tallyui/theme';
4
+ import { type VariantProps } from 'class-variance-authority';
5
+ import { toggleVariants, toggleTextVariants } from '../toggle';
6
+
7
+ type ToggleGroupProps = React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &
8
+ VariantProps<typeof toggleVariants>;
9
+
10
+ const ToggleGroupContext = React.createContext<VariantProps<typeof toggleVariants>>({
11
+ size: 'default',
12
+ variant: 'default',
13
+ });
14
+
15
+ const ToggleGroup = React.forwardRef<
16
+ React.ComponentRef<typeof ToggleGroupPrimitive.Root>,
17
+ ToggleGroupProps
18
+ >(function ToggleGroup({ className, variant, size, children, ...props }, ref) {
19
+ return (
20
+ <ToggleGroupPrimitive.Root
21
+ ref={ref}
22
+ className={cn('flex flex-row items-center justify-center gap-1', className)}
23
+ {...props}
24
+ >
25
+ <ToggleGroupContext.Provider value={{ variant, size }}>
26
+ {children}
27
+ </ToggleGroupContext.Provider>
28
+ </ToggleGroupPrimitive.Root>
29
+ );
30
+ });
31
+ ToggleGroup.displayName = 'ToggleGroup';
32
+
33
+ type ToggleGroupItemProps = React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &
34
+ VariantProps<typeof toggleVariants>;
35
+
36
+ const ToggleGroupItem = React.forwardRef<
37
+ React.ComponentRef<typeof ToggleGroupPrimitive.Item>,
38
+ ToggleGroupItemProps
39
+ >(function ToggleGroupItem({ className, variant, size, ...props }, ref) {
40
+ const context = React.useContext(ToggleGroupContext);
41
+
42
+ return (
43
+ <TextClassContext.Provider
44
+ value={toggleTextVariants({
45
+ variant: variant || context.variant,
46
+ size: size || context.size,
47
+ })}
48
+ >
49
+ <ToggleGroupPrimitive.Item
50
+ ref={ref}
51
+ className={cn(
52
+ toggleVariants({
53
+ variant: variant || context.variant,
54
+ size: size || context.size,
55
+ }),
56
+ className,
57
+ )}
58
+ {...props}
59
+ />
60
+ </TextClassContext.Provider>
61
+ );
62
+ });
63
+ ToggleGroupItem.displayName = 'ToggleGroupItem';
64
+
65
+ export { ToggleGroup, ToggleGroupItem };
66
+ export type { ToggleGroupProps, ToggleGroupItemProps };
@@ -0,0 +1,30 @@
1
+ import * as React from 'react';
2
+ import { Tooltip as TooltipPrimitive, TextClassContext } from '@tallyui/primitives';
3
+ import { cn } from '@tallyui/theme';
4
+
5
+ const Tooltip = TooltipPrimitive.Root;
6
+ const TooltipTrigger = TooltipPrimitive.Trigger;
7
+
8
+ const TooltipContent = React.forwardRef<
9
+ React.ComponentRef<typeof TooltipPrimitive.Content>,
10
+ React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
11
+ >(function TooltipContent({ className, sideOffset = 4, ...props }, ref) {
12
+ return (
13
+ <TooltipPrimitive.Portal>
14
+ <TextClassContext.Provider value="text-sm text-popover-foreground">
15
+ <TooltipPrimitive.Content
16
+ ref={ref}
17
+ sideOffset={sideOffset}
18
+ className={cn(
19
+ 'z-50 overflow-hidden rounded-md border border-border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md',
20
+ className,
21
+ )}
22
+ {...props}
23
+ />
24
+ </TextClassContext.Provider>
25
+ </TooltipPrimitive.Portal>
26
+ );
27
+ });
28
+ TooltipContent.displayName = 'TooltipContent';
29
+
30
+ export { Tooltip, TooltipTrigger, TooltipContent };
@@ -0,0 +1,43 @@
1
+ import * as React from 'react';
2
+ import { View, type ViewProps } from 'react-native';
3
+ import { cn } from '@tallyui/theme';
4
+ import { cva, type VariantProps } from 'class-variance-authority';
5
+
6
+ const vstackVariants = cva('flex-col', {
7
+ variants: {
8
+ space: {
9
+ none: 'gap-0',
10
+ xs: 'gap-1',
11
+ sm: 'gap-2',
12
+ md: 'gap-3',
13
+ lg: 'gap-4',
14
+ xl: 'gap-6',
15
+ },
16
+ reversed: {
17
+ true: 'flex-col-reverse',
18
+ },
19
+ },
20
+ defaultVariants: {
21
+ space: 'md',
22
+ },
23
+ });
24
+
25
+ type VStackProps = ViewProps & VariantProps<typeof vstackVariants>;
26
+
27
+ const VStack = React.forwardRef<View, VStackProps>(function VStack(
28
+ { className, space, reversed, ...props },
29
+ ref
30
+ ) {
31
+ return (
32
+ <View
33
+ ref={ref}
34
+ className={cn(vstackVariants({ space, reversed }), className)}
35
+ {...props}
36
+ />
37
+ );
38
+ });
39
+
40
+ VStack.displayName = 'VStack';
41
+
42
+ export { VStack, vstackVariants };
43
+ export type { VStackProps };
@@ -2,17 +2,25 @@
2
2
  * Type augmentations for React Native components to support className prop.
3
3
  * Uniwind provides these at runtime — this file ensures TypeScript accepts them.
4
4
  */
5
- export {};
5
+ import 'react-native';
6
6
 
7
7
  declare module 'react-native' {
8
8
  interface ViewProps {
9
9
  className?: string;
10
+ // react-native-web turns each entry into a data-* DOM attribute (e.g. { print: 'hide' } → data-print="hide").
11
+ // TV6a's first use: the print-style hook (`dataSet={{ print: 'hide' }}`) that TV6b lifts.
12
+ dataSet?: { [key: string]: string | number };
10
13
  }
11
14
 
12
15
  interface TextProps {
13
16
  className?: string;
14
17
  }
15
18
 
19
+ interface TextInputProps {
20
+ // Uniwind reads an accent-* class for the placeholder colour.
21
+ placeholderTextColorClassName?: string;
22
+ }
23
+
16
24
  interface ImagePropsBase {
17
25
  className?: string;
18
26
  }
@@ -1,53 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { render, screen } from '@testing-library/react';
3
- import { ConnectorProvider } from '@tallyui/core';
4
- import { CartLine } from '../cart/cart-line';
5
- import { createTestConnector, wooDoc, medusaDoc } from './helpers';
6
-
7
- describe('CartLine', () => {
8
- it('renders product name and line total for WooCommerce', () => {
9
- const connector = createTestConnector('woo');
10
- render(
11
- <ConnectorProvider connector={connector}>
12
- <CartLine item={{ doc: wooDoc, quantity: 2 }} />
13
- </ConnectorProvider>
14
- );
15
- expect(screen.getByText('Espresso Machine Pro')).toBeDefined();
16
- expect(screen.getByText('$1299.00 × 2')).toBeDefined();
17
- expect(screen.getByText('$2598.00')).toBeDefined();
18
- });
19
-
20
- it('renders product name and line total for Medusa', () => {
21
- const connector = createTestConnector('medusa');
22
- render(
23
- <ConnectorProvider connector={connector}>
24
- <CartLine item={{ doc: medusaDoc, quantity: 1 }} />
25
- </ConnectorProvider>
26
- );
27
- expect(screen.getByText('Espresso Machine Pro')).toBeDefined();
28
- expect(screen.getByText('$1299.00 × 1')).toBeDefined();
29
- expect(screen.getByText('$1299.00')).toBeDefined();
30
- });
31
-
32
- it('renders custom currency symbol', () => {
33
- const connector = createTestConnector('woo');
34
- render(
35
- <ConnectorProvider connector={connector}>
36
- <CartLine item={{ doc: wooDoc, quantity: 1 }} currencySymbol="€" />
37
- </ConnectorProvider>
38
- );
39
- expect(screen.getByText('€1299.00 × 1')).toBeDefined();
40
- expect(screen.getByText('€1299.00')).toBeDefined();
41
- });
42
-
43
- it('handles missing price gracefully', () => {
44
- const connector = createTestConnector('woo');
45
- render(
46
- <ConnectorProvider connector={connector}>
47
- <CartLine item={{ doc: {}, quantity: 3 }} />
48
- </ConnectorProvider>
49
- );
50
- expect(screen.getByText('× 3')).toBeDefined();
51
- expect(screen.getByText('—')).toBeDefined();
52
- });
53
- });
@@ -1,55 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { render, screen } from '@testing-library/react';
3
- import { Text } from 'react-native';
4
- import { ConnectorProvider } from '@tallyui/core';
5
- import { CartPanel } from '../cart/cart-panel';
6
- import { CartLine } from '../cart/cart-line';
7
- import { createTestConnector, wooDoc } from './helpers';
8
-
9
- describe('CartPanel', () => {
10
- const items = [
11
- { doc: wooDoc, quantity: 2 },
12
- { doc: { ...wooDoc, id: 2, name: 'Grinder', price: '199.99' }, quantity: 1 },
13
- ];
14
-
15
- it('renders cart line items', () => {
16
- const connector = createTestConnector('woo');
17
- render(
18
- <ConnectorProvider connector={connector}>
19
- <CartPanel items={items} renderItem={(item) => <CartLine item={item} />} />
20
- </ConnectorProvider>
21
- );
22
- expect(screen.getByText('Espresso Machine Pro')).toBeDefined();
23
- expect(screen.getByText('Grinder')).toBeDefined();
24
- });
25
-
26
- it('renders header and footer slots', () => {
27
- const connector = createTestConnector('woo');
28
- render(
29
- <ConnectorProvider connector={connector}>
30
- <CartPanel
31
- items={items}
32
- renderItem={(item) => <CartLine item={item} />}
33
- header={<Text>Customer: Jane</Text>}
34
- footer={<Text>Total: $1399.97</Text>}
35
- />
36
- </ConnectorProvider>
37
- );
38
- expect(screen.getByText('Customer: Jane')).toBeDefined();
39
- expect(screen.getByText('Total: $1399.97')).toBeDefined();
40
- });
41
-
42
- it('renders emptyState when no items', () => {
43
- const connector = createTestConnector('woo');
44
- render(
45
- <ConnectorProvider connector={connector}>
46
- <CartPanel
47
- items={[]}
48
- renderItem={() => null}
49
- emptyState={<Text>Cart is empty</Text>}
50
- />
51
- </ConnectorProvider>
52
- );
53
- expect(screen.getByText('Cart is empty')).toBeDefined();
54
- });
55
- });
@@ -1,60 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { render, screen } from '@testing-library/react';
3
- import { ConnectorProvider } from '@tallyui/core';
4
- import { CartTotal } from '../cart/cart-total';
5
- import { createTestConnector, wooDoc } from './helpers';
6
-
7
- const cartItems = [
8
- { doc: wooDoc, quantity: 2 },
9
- { doc: { ...wooDoc, name: 'Milk Frother', price: '49.99' }, quantity: 1 },
10
- ];
11
-
12
- describe('CartTotal', () => {
13
- it('renders subtotal and total labels', () => {
14
- const connector = createTestConnector('woo');
15
- const { container } = render(
16
- <ConnectorProvider connector={connector}>
17
- <CartTotal items={cartItems} />
18
- </ConnectorProvider>
19
- );
20
- expect(screen.getByText('Subtotal')).toBeDefined();
21
- expect(screen.getByText('Total')).toBeDefined();
22
- // subtotal and total both $2647.99 (no tax), so 2 elements
23
- expect(screen.getAllByText('$2647.99')).toHaveLength(2);
24
- });
25
-
26
- it('renders tax when taxRate is provided', () => {
27
- const connector = createTestConnector('woo');
28
- render(
29
- <ConnectorProvider connector={connector}>
30
- <CartTotal items={cartItems} taxRate={0.1} />
31
- </ConnectorProvider>
32
- );
33
- expect(screen.getByText('Tax')).toBeDefined();
34
- expect(screen.getByText('$264.80')).toBeDefined();
35
- expect(screen.getByText('$2912.79')).toBeDefined();
36
- });
37
-
38
- it('uses custom currency symbol and tax label', () => {
39
- const connector = createTestConnector('woo');
40
- render(
41
- <ConnectorProvider connector={connector}>
42
- <CartTotal items={cartItems} currencySymbol="€" taxRate={0.2} taxLabel="VAT" />
43
- </ConnectorProvider>
44
- );
45
- expect(screen.getByText('VAT')).toBeDefined();
46
- // Multiple euro amounts present — just check at least one exists
47
- expect(screen.getAllByText(/€/).length).toBeGreaterThan(0);
48
- });
49
-
50
- it('handles empty cart', () => {
51
- const connector = createTestConnector('woo');
52
- render(
53
- <ConnectorProvider connector={connector}>
54
- <CartTotal items={[]} />
55
- </ConnectorProvider>
56
- );
57
- // subtotal and total both $0.00
58
- expect(screen.getAllByText('$0.00')).toHaveLength(2);
59
- });
60
- });
@@ -1,47 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { render, screen } from '@testing-library/react';
3
- import { ConnectorProvider } from '@tallyui/core';
4
- import { CustomerCard } from '../customer/customer-card';
5
- import { createTestConnector, wooCustomerDoc } from './helpers';
6
-
7
- describe('CustomerCard', () => {
8
- it('renders the customer name', () => {
9
- const connector = createTestConnector('woo');
10
- render(
11
- <ConnectorProvider connector={connector}>
12
- <CustomerCard doc={wooCustomerDoc} />
13
- </ConnectorProvider>
14
- );
15
- expect(screen.getByText('Jane Smith')).toBeDefined();
16
- });
17
-
18
- it('renders the customer email', () => {
19
- const connector = createTestConnector('woo');
20
- render(
21
- <ConnectorProvider connector={connector}>
22
- <CustomerCard doc={wooCustomerDoc} />
23
- </ConnectorProvider>
24
- );
25
- expect(screen.getByText('jane@example.com')).toBeDefined();
26
- });
27
-
28
- it('renders the address summary', () => {
29
- const connector = createTestConnector('woo');
30
- render(
31
- <ConnectorProvider connector={connector}>
32
- <CustomerCard doc={wooCustomerDoc} />
33
- </ConnectorProvider>
34
- );
35
- expect(screen.getByText('123 Main St, Springfield, IL')).toBeDefined();
36
- });
37
-
38
- it('renders gracefully with empty doc', () => {
39
- const connector = createTestConnector('woo');
40
- const { container } = render(
41
- <ConnectorProvider connector={connector}>
42
- <CustomerCard doc={{}} />
43
- </ConnectorProvider>
44
- );
45
- expect(container).toBeDefined();
46
- });
47
- });
@@ -1,33 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { render, screen } from '@testing-library/react';
3
- import { ConnectorProvider } from '@tallyui/core';
4
- import { CustomerSelect } from '../customer/customer-select';
5
- import { createTestConnector, wooCustomerDoc } from './helpers';
6
-
7
- describe('CustomerSelect', () => {
8
- const customers = [
9
- wooCustomerDoc,
10
- { ...wooCustomerDoc, id: 2, first_name: 'Bob', last_name: 'Jones', email: 'bob@example.com' },
11
- ];
12
-
13
- it('renders customer results', () => {
14
- const connector = createTestConnector('woo');
15
- render(
16
- <ConnectorProvider connector={connector}>
17
- <CustomerSelect customers={customers} onSelect={() => {}} onSearch={() => {}} />
18
- </ConnectorProvider>
19
- );
20
- expect(screen.getByText('Jane Smith')).toBeDefined();
21
- expect(screen.getByText('Bob Jones')).toBeDefined();
22
- });
23
-
24
- it('shows selected customer name', () => {
25
- const connector = createTestConnector('woo');
26
- render(
27
- <ConnectorProvider connector={connector}>
28
- <CustomerSelect customers={[]} selected={wooCustomerDoc} onSelect={() => {}} onSearch={() => {}} />
29
- </ConnectorProvider>
30
- );
31
- expect(screen.getByText('Jane Smith')).toBeDefined();
32
- });
33
- });
@@ -1,40 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import { render, screen, fireEvent } from '@testing-library/react';
3
- import { FilterChip, FilterChipGroup } from '../input/filter-chip';
4
-
5
- describe('FilterChip', () => {
6
- it('renders the label', () => {
7
- render(<FilterChip label="Equipment" active={false} onPress={() => {}} />);
8
- expect(screen.getByText('Equipment')).toBeDefined();
9
- });
10
-
11
- it('calls onPress when tapped', () => {
12
- const onPress = vi.fn();
13
- render(<FilterChip label="Equipment" active={false} onPress={onPress} />);
14
- fireEvent.click(screen.getByText('Equipment'));
15
- expect(onPress).toHaveBeenCalled();
16
- });
17
- });
18
-
19
- describe('FilterChipGroup', () => {
20
- it('renders multiple chips', () => {
21
- const chips = [
22
- { label: 'Equipment', active: true },
23
- { label: 'Accessories', active: false },
24
- ];
25
- render(<FilterChipGroup chips={chips} onChipPress={() => {}} />);
26
- expect(screen.getByText('Equipment')).toBeDefined();
27
- expect(screen.getByText('Accessories')).toBeDefined();
28
- });
29
-
30
- it('calls onChipPress with the chip index', () => {
31
- const onChipPress = vi.fn();
32
- const chips = [
33
- { label: 'Equipment', active: false },
34
- { label: 'Accessories', active: false },
35
- ];
36
- render(<FilterChipGroup chips={chips} onChipPress={onChipPress} />);
37
- fireEvent.click(screen.getByText('Accessories'));
38
- expect(onChipPress).toHaveBeenCalledWith(1);
39
- });
40
- });