pixelize-design-library 2.3.16 → 2.3.19

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 (37) hide show
  1. package/.cursor/modules/inputs-date-file/MODULE.md +1 -0
  2. package/.cursor/modules/layout-navigation/MODULE.md +46 -1
  3. package/dist/App.d.ts.map +1 -1
  4. package/dist/Assets/defaultLogo.tsx +30 -0
  5. package/dist/Components/DatePicker/SingleDatePicker.d.ts.map +1 -1
  6. package/dist/Components/DatePicker/SingleDatePicker.js +30 -2
  7. package/dist/Components/Input/textInputIconSize.test.d.ts +1 -0
  8. package/dist/Components/Input/textInputIconSize.test.js +27 -0
  9. package/dist/Components/KanbanBoard/AccountCard.test.d.ts +1 -0
  10. package/dist/Components/KanbanBoard/AccountCard.test.js +68 -0
  11. package/dist/Components/KanbanBoard/KanbanActions/KanbanActions.test.d.ts +1 -0
  12. package/dist/Components/KanbanBoard/KanbanActions/KanbanActions.test.js +49 -0
  13. package/dist/Components/KanbanBoard/KanbanBoard.test.d.ts +1 -0
  14. package/dist/Components/KanbanBoard/KanbanBoard.test.js +185 -0
  15. package/dist/Components/SearchSelect/searchSelectSize.test.d.ts +1 -0
  16. package/dist/Components/SearchSelect/searchSelectSize.test.js +22 -0
  17. package/dist/Components/SideBar/SideBar.d.ts.map +1 -1
  18. package/dist/Components/SideBar/SideBar.js +4 -2
  19. package/dist/Components/SideBar/components/OtherApps.test.d.ts +1 -0
  20. package/dist/Components/SideBar/components/OtherApps.test.js +111 -0
  21. package/dist/Components/Table/components/TableBody.virtualize.test.d.ts +1 -0
  22. package/dist/Components/Table/components/TableBody.virtualize.test.js +93 -0
  23. package/dist/Components/Table/settings/ManageColumns.test.d.ts +1 -0
  24. package/dist/Components/Table/settings/ManageColumns.test.js +146 -0
  25. package/dist/Hooks/useSidebarPrefs.d.ts +67 -0
  26. package/dist/Hooks/useSidebarPrefs.d.ts.map +1 -0
  27. package/dist/Hooks/useSidebarPrefs.js +72 -0
  28. package/dist/Theme/buildBrandTokens.test.d.ts +1 -0
  29. package/dist/Theme/buildBrandTokens.test.js +31 -0
  30. package/dist/Theme/chartColorsFromTheme.test.d.ts +1 -0
  31. package/dist/Theme/chartColorsFromTheme.test.js +35 -0
  32. package/dist/Utils/tableNormalizeCellValue.test.d.ts +1 -0
  33. package/dist/Utils/tableNormalizeCellValue.test.js +41 -0
  34. package/dist/index.d.ts +3 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +3 -1
  37. package/package.json +1 -1
@@ -12,6 +12,7 @@ ThemeDatePicker (Single/Range/Time variants), FileUpload, FileUploader, Editor (
12
12
  ## Key rules
13
13
 
14
14
  - DatePicker themed for brand tokens
15
+ - Masked-input blur must not discard a valid value. `isMaskComplete()` only recognises numeric/`a` tokens, so text month formats (e.g. `MMM d, yyyy`) never look "complete" — `handleInputBlur`/`handleInputFocus` therefore keep any value that `parseValidatedValue()` can parse to a valid date, and only `requestClose(null)` (clear) on empty/unparseable input. The Clear button and emptied-mask paths remain the explicit ways to clear. Regression: `SingleDatePicker.blur.test.tsx`.
15
16
 
16
17
  ## Migration notes
17
18
 
@@ -28,6 +28,8 @@ layout flow, so content stays full-width — not shrunk):
28
28
  - NavigationBar hamburger now shows below `lg` (was `< md`); its panel icon is
29
29
  state-aware (`PanelLeftClose` open / `PanelRightClose` closed).
30
30
  - The overlay panel has **square edges** (no border radius).
31
+ - Overlay panel width = `OVERLAY_WIDTH` (**14.7rem** / 235px — a touch wider than the
32
+ inline expanded 13rem for touch), capped by `maxW: 88vw` on very small screens.
31
33
 
32
34
  Breakpoint, scrim/slide, and z-index are Chakra responsive props (no JS flash) when
33
35
  `mobileNav` is off; the breakpoint is read once via `useBreakpointValue({ base: true, lg: false })`.
@@ -50,9 +52,52 @@ a close: toggling never hides the navigation.
50
52
  - Selecting a **leaf** menu item in overlay mode auto-closes the overlay (content returns
51
53
  to full-screen); submenu **parents** only expand and do not close it.
52
54
 
55
+ ## Persisting sidebar choices — `useSidebarPrefs`
56
+
57
+ Opt-in hook (`src/Hooks/useSidebarPrefs.ts`, exported from `index.ts`) that persists the
58
+ user's **explicit** layout choices to `localStorage` so they survive reload/restart —
59
+ without it, `mobileNav`/collapse reset to desktop default on every mount.
60
+
61
+ - Persists `{ mobileNav, collapsed, mobileOpen }` so the sidebar restores to **exactly**
62
+ the same state on refresh / new session (mode, mini-rail, and whether the drawer is
63
+ open). It deliberately does **not** persist the `lg` breakpoint (viewport-driven, must
64
+ stay live).
65
+ - SSR-safe (`typeof window` guards); corrupt/invalid stored values fall back to defaults
66
+ per field; `setItem` failures (private mode / quota) no-op without throwing.
67
+ - API: state `{ mobileNav, collapsed, mobileOpen }`; setters `setMobileNav / setCollapsed
68
+ / setMobileOpen`; toggles `toggleMobileNav / toggleCollapsed / toggleMobileOpen`; plus
69
+ **composed handlers** `onToggleMobileNav` (flips navbar mode **and** syncs the drawer)
70
+ and `closeMobile`. Options: `storageKey` (default `pixelize:sidebar-prefs:v1`, versioned)
71
+ and `defaults`.
72
+ - **Consumer adoption** — map the returned values straight onto `SideBar` + `NavBar`; the
73
+ composed handlers mean apps don't re-implement the navbar-mode/drawer behavior:
74
+
75
+ ```tsx
76
+ const sb = useSidebarPrefs();
77
+ <SideBar
78
+ toggle={sb.collapsed} changeToggle={sb.toggleCollapsed}
79
+ mobileNav={sb.mobileNav} onToggleMobileNav={sb.onToggleMobileNav}
80
+ mobileOpen={sb.mobileOpen} onMobileClose={sb.closeMobile}
81
+ /* ...menus, user, etc. */
82
+ />
83
+ <NavBar
84
+ sideBarToggole={sb.mobileOpen} onSideBarToggole={sb.toggleMobileOpen}
85
+ forceSideBarToggle={sb.mobileNav} /* ...nav props */
86
+ />
87
+ ```
88
+
89
+ Per-app override: pass `storageKey` if an app needs a separate persisted namespace.
90
+ See `App.tsx` for the live reference wiring.
91
+
53
92
  ### Known gaps
54
93
 
55
94
  - `SecondaryBar` keeps its current side-by-side stacking inside the overlay — revisit if
56
95
  it feels cramped on very narrow screens.
57
96
  - Consumer apps (crm/account/…) must pass `mobileOpen`/`onMobileClose` to adopt overlay
58
- mode; until then they get the unchanged inline sidebar.
97
+ mode, and adopt `useSidebarPrefs` to get persistence; until then they get the unchanged
98
+ inline sidebar with non-persisted state.
99
+ - Persisted `collapsed` can be transiently overridden by SideBar's existing "auto-collapse
100
+ when a secondary panel is active" effect — pre-existing behavior, not regressed.
101
+ - Persisting `mobileOpen` means a real phone (`belowLg`) that was left with the drawer open
102
+ reloads with it open (covering content until a leaf tap auto-closes it). Accepted to honor
103
+ "restore exactly the same state"; revisit with a `belowLg` gate if it proves annoying.
package/dist/App.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../src/App.tsx"],"names":[],"mappings":"AAQA,OAAO,cAAc,CAAC;AAyDtB,iBAAS,GAAG,4CAkFX;AAED,eAAe,GAAG,CAAC"}
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../src/App.tsx"],"names":[],"mappings":"AASA,OAAO,cAAc,CAAC;AAyDtB,iBAAS,GAAG,4CAiFX;AAED,eAAe,GAAG,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { useCustomTheme } from "../Theme/useCustomTheme";
2
+
3
+ const DefaultLogo = () => {
4
+ const theme = useCustomTheme();
5
+ return (
6
+ <svg
7
+ width="12.09"
8
+ height="15.95"
9
+ viewBox="0 0 31 29"
10
+ fill="none"
11
+ xmlns="http://www.w3.org/2000/svg"
12
+ >
13
+ <path
14
+ d="M30.4999 6.26137V7.81058H15.9761H14.8787V22.8508V28.8862H12.5226C9.6824 28.8862 7.32632 26.4656 7.06812 23.3995C7.06812 23.2381 7.06812 23.0444 7.06812 22.8508V18.3323C7.06812 18.2677 7.06812 18.2032 7.06812 18.1063V15.5889V6.2291C7.06812 2.80793 9.84378 -5.72205e-06 13.2972 -5.72205e-06H24.8517C28.0147 0.355021 30.4676 3.00158 30.4676 6.2291L30.4999 6.26137Z"
15
+ fill={theme.colors.black}
16
+ />
17
+ <path
18
+ d="M7.10053 23.3672H7.03598C5.16402 23.2381 3.48571 22.4635 2.25926 21.3338C0.871428 20.0428 0 18.2677 0 16.3312V15.6211H7.06825V18.1386C7.06825 18.2032 7.06825 18.2677 7.06825 18.3645V22.883C7.06825 23.0767 7.06825 23.2381 7.06825 23.4317L7.10053 23.3672Z"
19
+ fill={theme.colors.black}
20
+ />
21
+ <path
22
+ d="M30.4678 12.0063V15.6211V17.1703C30.4678 20.6238 26.272 23.3994 21.0757 23.3994H14.8789V22.8508V15.6211H22.6572V7.81056C22.6895 7.81056 30.5001 7.81056 30.5001 7.81056V11.5868C30.5001 11.7159 30.5001 11.8772 30.5001 12.0063H30.4678Z"
23
+ fill={theme.colors.black}
24
+ />
25
+ </svg>
26
+
27
+ )
28
+ };
29
+
30
+ export default DefaultLogo;
@@ -1 +1 @@
1
- {"version":3,"file":"SingleDatePicker.d.ts","sourceRoot":"","sources":["../../../src/Components/DatePicker/SingleDatePicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA6E,MAAM,OAAO,CAAC;AA6BlG,OAAO,EAAE,qBAAqB,EAAc,MAAM,mBAAmB,CAAC;AA2OtE,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA+7B5D,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"SingleDatePicker.d.ts","sourceRoot":"","sources":["../../../src/Components/DatePicker/SingleDatePicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA6E,MAAM,OAAO,CAAC;AA6BlG,OAAO,EAAE,qBAAqB,EAAc,MAAM,mBAAmB,CAAC;AA2OtE,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA09B5D,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -435,7 +435,13 @@ const SingleDatePicker = (props) => {
435
435
  if (enableMask && isMaskActive) {
436
436
  setIsMaskActive(false);
437
437
  segmentInputBufferRef.current = "";
438
- if (!value || !value.trim() || !isMaskComplete(value)) {
438
+ // isMaskComplete() can't recognise text month tokens (e.g. "MMM"), so a
439
+ // valid, externally-set value like "Jun 10, 2026" is judged "incomplete"
440
+ // and was being cleared on blur (data loss). Only clear when the value is
441
+ // empty or genuinely can't be parsed into a valid date; otherwise fall
442
+ // through and let the parse path below commit it.
443
+ const hasParsableDate = parseValidatedValue(value) !== null;
444
+ if (!value || !value.trim() || (!isMaskComplete(value) && !hasParsableDate)) {
439
445
  setInputValue("");
440
446
  setTempDate(null);
441
447
  setIsTyping(false);
@@ -635,6 +641,24 @@ const SingleDatePicker = (props) => {
635
641
  }
636
642
  return finalDate;
637
643
  };
644
+ // Returns the validated Date for a fully-typed value, or null when the value
645
+ // is empty / not a parseable, in-range date. Used by blur handling so an
646
+ // already-valid value is never discarded just because the mask-completeness
647
+ // heuristic doesn't understand text month tokens.
648
+ const parseValidatedValue = (value) => {
649
+ if (!value || !value.trim())
650
+ return null;
651
+ try {
652
+ const parsed = (0, date_fns_1.parse)(value, resolvedDateFormat, new Date());
653
+ if ((0, date_fns_1.isValid)(parsed)) {
654
+ return computeFinalDate(parsed);
655
+ }
656
+ }
657
+ catch (error) {
658
+ console.error("Error parsing input:", error);
659
+ }
660
+ return null;
661
+ };
638
662
  const updateTempDateFromMask = (value) => {
639
663
  if (!isMaskComplete(value)) {
640
664
  return;
@@ -662,7 +686,11 @@ const SingleDatePicker = (props) => {
662
686
  return;
663
687
  setIsMaskActive(true);
664
688
  setIsTyping(true);
665
- if (!inputValue || inputValue.length !== maskPlaceholder.length) {
689
+ // Never blank an already-valid value on focus. The mask placeholder is built
690
+ // with padded tokens (e.g. "MMM dd, yyyy"), so a valid but shorter value such
691
+ // as "Jun 5, 2026" would otherwise be replaced by the empty mask and lost.
692
+ const hasValidValue = parseValidatedValue(inputValue) !== null;
693
+ if (!hasValidValue && (!inputValue || inputValue.length !== maskPlaceholder.length)) {
666
694
  setInputValue(maskPlaceholder);
667
695
  maskInsertedRef.current = true;
668
696
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ var textInputIconSize_1 = require("./textInputIconSize");
4
+ describe("getTextInputIconSizePx", function () {
5
+ it("maps Chakra input sizes to increasing pixel values", function () {
6
+ var xs = (0, textInputIconSize_1.getTextInputIconSizePx)("xs");
7
+ var sm = (0, textInputIconSize_1.getTextInputIconSizePx)("sm");
8
+ var md = (0, textInputIconSize_1.getTextInputIconSizePx)("md");
9
+ var lg = (0, textInputIconSize_1.getTextInputIconSizePx)("lg");
10
+ expect(xs).toBeLessThan(sm);
11
+ expect(sm).toBeLessThan(md);
12
+ expect(md).toBeLessThan(lg);
13
+ });
14
+ it("defaults to md for missing or unknown size", function () {
15
+ expect((0, textInputIconSize_1.getTextInputIconSizePx)(undefined)).toBe(18);
16
+ expect((0, textInputIconSize_1.getTextInputIconSizePx)("nope")).toBe(18);
17
+ });
18
+ });
19
+ describe("getTextInputIconWrapperSx", function () {
20
+ it("includes the resolved pixel size in svg rules for xs", function () {
21
+ var sx = (0, textInputIconSize_1.getTextInputIconWrapperSx)("xs");
22
+ var svg = sx["& > svg, & > * svg, & svg"];
23
+ expect(svg).toBeDefined();
24
+ expect(svg === null || svg === void 0 ? void 0 : svg.width).toContain("14");
25
+ expect(svg === null || svg === void 0 ? void 0 : svg.height).toContain("14");
26
+ });
27
+ });
@@ -0,0 +1 @@
1
+ import "@testing-library/jest-dom";
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ var react_1 = __importDefault(require("react"));
18
+ var react_2 = require("@testing-library/react");
19
+ require("@testing-library/jest-dom");
20
+ var react_3 = require("@chakra-ui/react");
21
+ var AccountCard_1 = __importDefault(require("./AccountCard"));
22
+ jest.mock("../../Theme/useCustomTheme", function () { return ({
23
+ useCustomTheme: function () { return ({
24
+ colors: {
25
+ background: { 50: "#fafafa", 100: "#f5f5f5", 500: "#fff" },
26
+ boxborder: { 300: "#e2e8f0" },
27
+ gray: { 200: "#eee", 700: "#333" },
28
+ primary: { 300: "#90cdf4", 500: "#3182ce" },
29
+ red: { 600: "#e53e3e" },
30
+ blue: { 600: "#3182ce" },
31
+ text: {},
32
+ },
33
+ }); },
34
+ }); });
35
+ var mockAccount = {
36
+ id: "1",
37
+ name: "Test Account",
38
+ email: "test@example.com",
39
+ };
40
+ var defaultProps = {
41
+ account: mockAccount,
42
+ index: 0,
43
+ onDelete: jest.fn(),
44
+ onOpen: jest.fn(),
45
+ };
46
+ var renderCard = function (ui) {
47
+ return (0, react_2.render)(react_1.default.createElement(react_3.ChakraProvider, null, ui));
48
+ };
49
+ describe("AccountCard – permissions", function () {
50
+ it("renders the delete icon when isDeletable is true (default)", function () {
51
+ renderCard(react_1.default.createElement(AccountCard_1.default, __assign({}, defaultProps)));
52
+ expect(react_2.screen.getByLabelText("Delete")).toBeInTheDocument();
53
+ });
54
+ it("hides the delete icon when isDeletable is false", function () {
55
+ renderCard(react_1.default.createElement(AccountCard_1.default, __assign({}, defaultProps, { isDeletable: false })));
56
+ expect(react_2.screen.queryByLabelText("Delete")).not.toBeInTheDocument();
57
+ });
58
+ it("renders the open (ExternalLink) icon regardless of isDeletable", function () {
59
+ renderCard(react_1.default.createElement(AccountCard_1.default, __assign({}, defaultProps, { isDeletable: false })));
60
+ expect(react_2.screen.getByLabelText("Open")).toBeInTheDocument();
61
+ });
62
+ it("calls onDelete when delete icon is clicked and isDeletable is true", function () {
63
+ var onDelete = jest.fn();
64
+ renderCard(react_1.default.createElement(AccountCard_1.default, __assign({}, defaultProps, { onDelete: onDelete, isDeletable: true })));
65
+ react_2.fireEvent.click(react_2.screen.getByLabelText("Delete"));
66
+ expect(onDelete).toHaveBeenCalledWith(mockAccount);
67
+ });
68
+ });
@@ -0,0 +1 @@
1
+ import "@testing-library/jest-dom";
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ var react_1 = __importDefault(require("react"));
7
+ var react_2 = require("@testing-library/react");
8
+ require("@testing-library/jest-dom");
9
+ var react_3 = require("@chakra-ui/react");
10
+ var KanbanActions_1 = __importDefault(require("./KanbanActions"));
11
+ jest.mock("../../../Theme/useCustomTheme", function () { return ({
12
+ useCustomTheme: function () { return ({
13
+ colors: {
14
+ text: { 500: "#555" },
15
+ primary: { 500: "#3182ce" },
16
+ },
17
+ }); },
18
+ }); });
19
+ jest.mock("../../Select/Select", function () { return ({
20
+ __esModule: true,
21
+ default: function (_a) {
22
+ var placeholder = _a.placeholder;
23
+ return react_1.default.createElement("div", { "data-testid": "mock-select" }, placeholder);
24
+ },
25
+ }); });
26
+ jest.mock("../../Divider/Divider", function () { return ({
27
+ __esModule: true,
28
+ default: function () { return react_1.default.createElement("div", { "data-testid": "mock-divider" }); },
29
+ }); });
30
+ var options = [{ id: "1", label: "Open" }];
31
+ var renderActions = function (ui) {
32
+ return (0, react_2.render)(react_1.default.createElement(react_3.ChakraProvider, null, ui));
33
+ };
34
+ describe("KanbanActions", function () {
35
+ it("renders Edit Layout and Create New as buttons", function () {
36
+ renderActions(react_1.default.createElement(KanbanActions_1.default, { options: options, onEdit: jest.fn(), onCreate: jest.fn() }));
37
+ expect(react_2.screen.getByRole("button", { name: /edit layout/i })).toBeInTheDocument();
38
+ expect(react_2.screen.getByRole("button", { name: /create new/i })).toBeInTheDocument();
39
+ });
40
+ it("invokes onEdit and onCreate when buttons are clicked", function () {
41
+ var onEdit = jest.fn();
42
+ var onCreate = jest.fn();
43
+ renderActions(react_1.default.createElement(KanbanActions_1.default, { options: options, onEdit: onEdit, onCreate: onCreate }));
44
+ react_2.fireEvent.click(react_2.screen.getByRole("button", { name: /edit layout/i }));
45
+ react_2.fireEvent.click(react_2.screen.getByRole("button", { name: /create new/i }));
46
+ expect(onEdit).toHaveBeenCalledTimes(1);
47
+ expect(onCreate).toHaveBeenCalledTimes(1);
48
+ });
49
+ });
@@ -0,0 +1 @@
1
+ import "@testing-library/jest-dom";
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ var react_1 = __importDefault(require("react"));
18
+ var react_2 = require("@testing-library/react");
19
+ require("@testing-library/jest-dom");
20
+ var react_3 = require("@chakra-ui/react");
21
+ var KanbanBoard_1 = __importDefault(require("./KanbanBoard"));
22
+ jest.mock("../../Theme/useCustomTheme", function () { return ({
23
+ useCustomTheme: function () { return ({
24
+ colors: {
25
+ background: { 100: "#f9f9f9", 500: "#fff" },
26
+ gray: { 100: "#f0f0f0", 200: "#eee", 300: "#ddd", 400: "#ccc", 500: "#bbb", 700: "#333" },
27
+ primary: { 500: "#3182ce" },
28
+ blue: { 50: "#ebf8ff", 500: "#3182ce", 600: "#3182ce" },
29
+ red: { 600: "#e53e3e" },
30
+ text: { 500: "#555", 600: "#444", 700: "#333" },
31
+ secondary: { 500: "#805ad5" },
32
+ },
33
+ }); },
34
+ }); });
35
+ jest.mock("@hello-pangea/dnd", function () { return ({
36
+ DragDropContext: function (_a) {
37
+ var children = _a.children;
38
+ return react_1.default.createElement("div", null, children);
39
+ },
40
+ Droppable: function (_a) {
41
+ var children = _a.children;
42
+ return children({ innerRef: function () { }, droppableProps: {}, placeholder: null }, { isDraggingOver: false });
43
+ },
44
+ Draggable: function (_a) {
45
+ var children = _a.children, isDragDisabled = _a.isDragDisabled;
46
+ var provided = {
47
+ innerRef: function () { },
48
+ draggableProps: { "data-drag-disabled": isDragDisabled },
49
+ dragHandleProps: {},
50
+ };
51
+ return children(provided, { isDragging: false });
52
+ },
53
+ }); });
54
+ jest.mock("react-window", function () { return ({
55
+ VariableSizeList: function () { return null; },
56
+ }); });
57
+ beforeAll(function () {
58
+ global.ResizeObserver = /** @class */ (function () {
59
+ function class_1() {
60
+ }
61
+ class_1.prototype.observe = function () { };
62
+ class_1.prototype.unobserve = function () { };
63
+ class_1.prototype.disconnect = function () { };
64
+ return class_1;
65
+ }());
66
+ Object.defineProperty(window, "matchMedia", {
67
+ writable: true,
68
+ configurable: true,
69
+ value: jest.fn().mockImplementation(function (query) { return ({
70
+ matches: false,
71
+ media: query,
72
+ onchange: null,
73
+ addListener: jest.fn(),
74
+ removeListener: jest.fn(),
75
+ addEventListener: jest.fn(),
76
+ removeEventListener: jest.fn(),
77
+ dispatchEvent: jest.fn(),
78
+ }); }),
79
+ });
80
+ });
81
+ var renderWithChakra = function (ui) {
82
+ return (0, react_2.render)(react_1.default.createElement(react_3.ChakraProvider, null, ui));
83
+ };
84
+ var sampleData = {
85
+ col1: {
86
+ id: "col1",
87
+ title: "To Do",
88
+ color: "#3182ce",
89
+ items: [
90
+ { id: "1", name: "Account A", email: "a@example.com" },
91
+ { id: "2", name: "Account B", email: "b@example.com" },
92
+ ],
93
+ },
94
+ };
95
+ describe("KanbanBoard – permissions", function () {
96
+ describe("isDeletable", function () {
97
+ it("shows card delete icons when permissions.isDeletable is true", function () {
98
+ renderWithChakra(react_1.default.createElement(KanbanBoard_1.default, { data: sampleData, permissions: { isDeletable: true } }));
99
+ expect(react_2.screen.getAllByLabelText("Delete").length).toBeGreaterThan(0);
100
+ });
101
+ it("hides card delete icons when permissions.isDeletable is false", function () {
102
+ renderWithChakra(react_1.default.createElement(KanbanBoard_1.default, { data: sampleData, permissions: { isDeletable: false } }));
103
+ expect(react_2.screen.queryByLabelText("Delete")).not.toBeInTheDocument();
104
+ });
105
+ it("shows card delete icons when permissions prop is omitted (default allow)", function () {
106
+ renderWithChakra(react_1.default.createElement(KanbanBoard_1.default, { data: sampleData }));
107
+ expect(react_2.screen.getAllByLabelText("Delete").length).toBeGreaterThan(0);
108
+ });
109
+ });
110
+ describe("isCreatable", function () {
111
+ var kanbanCreate = { isCreatable: true, label: "Create New" };
112
+ it("shows the create button when permissions.isCreatable is true", function () {
113
+ renderWithChakra(react_1.default.createElement(KanbanBoard_1.default, { data: sampleData, kanbanCreate: kanbanCreate, permissions: { isCreatable: true } }));
114
+ expect(react_2.screen.getByText("Create New")).toBeInTheDocument();
115
+ });
116
+ it("hides the create button when permissions.isCreatable is false", function () {
117
+ renderWithChakra(react_1.default.createElement(KanbanBoard_1.default, { data: sampleData, kanbanCreate: kanbanCreate, permissions: { isCreatable: false } }));
118
+ expect(react_2.screen.queryByText("Create New")).not.toBeInTheDocument();
119
+ });
120
+ });
121
+ describe("isEditable", function () {
122
+ var kanbanEdit = { isEditable: true, label: "Edit Layout" };
123
+ it("shows the edit button when permissions.isEditable is true", function () {
124
+ renderWithChakra(react_1.default.createElement(KanbanBoard_1.default, { data: sampleData, kanbanEdit: kanbanEdit, permissions: { isEditable: true } }));
125
+ expect(react_2.screen.getByText("Edit Layout")).toBeInTheDocument();
126
+ });
127
+ it("hides the edit button when permissions.isEditable is false", function () {
128
+ renderWithChakra(react_1.default.createElement(KanbanBoard_1.default, { data: sampleData, kanbanEdit: kanbanEdit, permissions: { isEditable: false } }));
129
+ expect(react_2.screen.queryByText("Edit Layout")).not.toBeInTheDocument();
130
+ });
131
+ it("sets isDragDisabled on cards when permissions.isEditable is false", function () {
132
+ renderWithChakra(react_1.default.createElement(KanbanBoard_1.default, { data: sampleData, permissions: { isEditable: false } }));
133
+ var disabledDraggables = react_2.screen
134
+ .getAllByRole("generic")
135
+ .filter(function (el) { return el.getAttribute("data-drag-disabled") === "true"; });
136
+ expect(disabledDraggables.length).toBeGreaterThan(0);
137
+ });
138
+ });
139
+ describe("column collapse", function () {
140
+ it("shows a collapse control per column when enableColumnCollapse is true", function () {
141
+ renderWithChakra(react_1.default.createElement(KanbanBoard_1.default, { data: sampleData, enableColumnCollapse: true }));
142
+ expect(react_2.screen.getAllByLabelText("Collapse column").length).toBeGreaterThan(0);
143
+ });
144
+ it("hides cards when a column is collapsed", function () {
145
+ renderWithChakra(react_1.default.createElement(KanbanBoard_1.default, { data: sampleData, enableColumnCollapse: true }));
146
+ expect(react_2.screen.getAllByLabelText("Delete").length).toBeGreaterThan(0);
147
+ react_2.fireEvent.click(react_2.screen.getAllByLabelText("Collapse column")[0]);
148
+ expect(react_2.screen.queryByLabelText("Delete")).not.toBeInTheDocument();
149
+ expect(react_2.screen.getByLabelText("Expand column")).toBeInTheDocument();
150
+ });
151
+ it("still shows column customNode when horizontally collapsed", function () {
152
+ var dataWithCustom = {
153
+ col1: __assign(__assign({}, sampleData.col1), { customNode: function () { return react_1.default.createElement("span", null, "Column summary line"); } }),
154
+ };
155
+ renderWithChakra(react_1.default.createElement(KanbanBoard_1.default, { data: dataWithCustom, enableColumnCollapse: true }));
156
+ expect(react_2.screen.getByText("Column summary line")).toBeInTheDocument();
157
+ react_2.fireEvent.click(react_2.screen.getByLabelText("Collapse column"));
158
+ expect(react_2.screen.queryByLabelText("Delete")).not.toBeInTheDocument();
159
+ expect(react_2.screen.getByText("Column summary line")).toBeInTheDocument();
160
+ });
161
+ it("starts with columns collapsed when collapsedColumns is set", function () {
162
+ renderWithChakra(react_1.default.createElement(KanbanBoard_1.default, { data: sampleData, enableColumnCollapse: true, collapsedColumns: { col1: true } }));
163
+ expect(react_2.screen.queryByLabelText("Delete")).not.toBeInTheDocument();
164
+ expect(react_2.screen.getByLabelText("Expand column")).toBeInTheDocument();
165
+ });
166
+ it("calls onColumnCollapseChange when collapsing and expanding", function () {
167
+ var onColumnCollapseChange = jest.fn();
168
+ renderWithChakra(react_1.default.createElement(KanbanBoard_1.default, { data: sampleData, enableColumnCollapse: true, onColumnCollapseChange: onColumnCollapseChange }));
169
+ react_2.fireEvent.click(react_2.screen.getAllByLabelText("Collapse column")[0]);
170
+ expect(onColumnCollapseChange).toHaveBeenCalledTimes(1);
171
+ expect(onColumnCollapseChange).toHaveBeenLastCalledWith({
172
+ colId: "col1",
173
+ collapsed: true,
174
+ collapsedColumns: { col1: true },
175
+ });
176
+ react_2.fireEvent.click(react_2.screen.getByLabelText("Expand column"));
177
+ expect(onColumnCollapseChange).toHaveBeenCalledTimes(2);
178
+ expect(onColumnCollapseChange).toHaveBeenLastCalledWith({
179
+ colId: "col1",
180
+ collapsed: false,
181
+ collapsedColumns: { col1: false },
182
+ });
183
+ });
184
+ });
185
+ });
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ var searchSelectSize_1 = require("./searchSelectSize");
4
+ describe("getSearchSelectSizeStyles", function () {
5
+ it.each(["xs", "sm", "md", "lg"])("returns minH for %s", function (sz) {
6
+ var t = (0, searchSelectSize_1.getSearchSelectSizeStyles)(sz);
7
+ expect(t.minH).toMatch(/rem$/);
8
+ expect(t.insideSelectDefaultHeight).toMatch(/rem$/);
9
+ });
10
+ it("xs is more compact than lg", function () {
11
+ var xs = (0, searchSelectSize_1.getSearchSelectSizeStyles)("xs");
12
+ var lg = (0, searchSelectSize_1.getSearchSelectSizeStyles)("lg");
13
+ var parse = function (h) { return parseFloat(h); };
14
+ expect(parse(xs.minH)).toBeLessThan(parse(lg.minH));
15
+ expect(parse(xs.insideSelectDefaultHeight)).toBeLessThan(parse(lg.insideSelectDefaultHeight));
16
+ });
17
+ it("uses md as fallback for unknown size keys (runtime map lookup)", function () {
18
+ var t = (0, searchSelectSize_1.getSearchSelectSizeStyles)("unknown");
19
+ var md = (0, searchSelectSize_1.getSearchSelectSizeStyles)("md");
20
+ expect(t).toEqual(md);
21
+ });
22
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"SideBar.d.ts","sourceRoot":"","sources":["../../../src/Components/SideBar/SideBar.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAe,MAAM,gBAAgB,CAAC;AAuZ3D,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,EAC9B,KAAK,EACL,UAAU,EACV,eAAe,EACf,uBAAuB,EACvB,MAAM,EACN,YAAY,EACZ,IAAI,EACJ,WAAW,EACX,eAAe,EACf,IAAI,EACJ,eAAuB,EACvB,aAAa,EACb,eAAe,EACf,qBAAqB,EACrB,SAAS,EACT,YAAY,EACZ,IAAW,EACX,SAAS,EACT,UAAkB,EAClB,aAAa,EACb,SAAiB,EACjB,iBAAiB,GAClB,EAAE,YAAY,2CAsRd"}
1
+ {"version":3,"file":"SideBar.d.ts","sourceRoot":"","sources":["../../../src/Components/SideBar/SideBar.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAe,MAAM,gBAAgB,CAAC;AAyZ3D,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,EAC9B,KAAK,EACL,UAAU,EACV,eAAe,EACf,uBAAuB,EACvB,MAAM,EACN,YAAY,EACZ,IAAI,EACJ,WAAW,EACX,eAAe,EACf,IAAI,EACJ,eAAuB,EACvB,aAAa,EACb,eAAe,EACf,qBAAqB,EACrB,SAAS,EACT,YAAY,EACZ,IAAW,EACX,SAAS,EACT,UAAkB,EAClB,aAAa,EACb,SAAiB,EACjB,iBAAiB,GAClB,EAAE,YAAY,2CAsRd"}
@@ -13,8 +13,10 @@ const lucide_react_1 = require("lucide-react");
13
13
  // Overlay stacking — mirrors theme.zIndices (overlay=1300 scrim, modal=1400 panel).
14
14
  const Z_SCRIM = 1300;
15
15
  const Z_PANEL = 1400;
16
- // Width of the slide-in overlay panel (a touch wider than the inline 13rem for touch).
17
- const OVERLAY_WIDTH = "16rem";
16
+ // Overlay/mobile panel width. ~14.7rem (235px): a touch wider than the inline
17
+ // expanded 13rem for comfortable touch targets, but below the old 16rem that was
18
+ // too wide on phones. Still capped by `maxW: 88vw` on very small screens.
19
+ const OVERLAY_WIDTH = "14.7rem";
18
20
  const MenuItems_1 = __importDefault(require("./components/MenuItems"));
19
21
  const defaultLogo_1 = __importDefault(require("../../Assets/defaultLogo"));
20
22
  const OverflowTooltipText_1 = __importDefault(require("./components/OverflowTooltipText"));
@@ -0,0 +1 @@
1
+ import "@testing-library/jest-dom";