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.
- package/.cursor/modules/inputs-date-file/MODULE.md +1 -0
- package/.cursor/modules/layout-navigation/MODULE.md +46 -1
- package/dist/App.d.ts.map +1 -1
- package/dist/Assets/defaultLogo.tsx +30 -0
- package/dist/Components/DatePicker/SingleDatePicker.d.ts.map +1 -1
- package/dist/Components/DatePicker/SingleDatePicker.js +30 -2
- package/dist/Components/Input/textInputIconSize.test.d.ts +1 -0
- package/dist/Components/Input/textInputIconSize.test.js +27 -0
- package/dist/Components/KanbanBoard/AccountCard.test.d.ts +1 -0
- package/dist/Components/KanbanBoard/AccountCard.test.js +68 -0
- package/dist/Components/KanbanBoard/KanbanActions/KanbanActions.test.d.ts +1 -0
- package/dist/Components/KanbanBoard/KanbanActions/KanbanActions.test.js +49 -0
- package/dist/Components/KanbanBoard/KanbanBoard.test.d.ts +1 -0
- package/dist/Components/KanbanBoard/KanbanBoard.test.js +185 -0
- package/dist/Components/SearchSelect/searchSelectSize.test.d.ts +1 -0
- package/dist/Components/SearchSelect/searchSelectSize.test.js +22 -0
- package/dist/Components/SideBar/SideBar.d.ts.map +1 -1
- package/dist/Components/SideBar/SideBar.js +4 -2
- package/dist/Components/SideBar/components/OtherApps.test.d.ts +1 -0
- package/dist/Components/SideBar/components/OtherApps.test.js +111 -0
- package/dist/Components/Table/components/TableBody.virtualize.test.d.ts +1 -0
- package/dist/Components/Table/components/TableBody.virtualize.test.js +93 -0
- package/dist/Components/Table/settings/ManageColumns.test.d.ts +1 -0
- package/dist/Components/Table/settings/ManageColumns.test.js +146 -0
- package/dist/Hooks/useSidebarPrefs.d.ts +67 -0
- package/dist/Hooks/useSidebarPrefs.d.ts.map +1 -0
- package/dist/Hooks/useSidebarPrefs.js +72 -0
- package/dist/Theme/buildBrandTokens.test.d.ts +1 -0
- package/dist/Theme/buildBrandTokens.test.js +31 -0
- package/dist/Theme/chartColorsFromTheme.test.d.ts +1 -0
- package/dist/Theme/chartColorsFromTheme.test.js +35 -0
- package/dist/Utils/tableNormalizeCellValue.test.d.ts +1 -0
- package/dist/Utils/tableNormalizeCellValue.test.js +41 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- 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
|
|
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":"
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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 @@
|
|
|
1
|
+
export {};
|
|
@@ -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;
|
|
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
|
-
//
|
|
17
|
-
|
|
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";
|