@evoke-platform/ui-components 1.6.0-dev.6 → 1.6.0-testing.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.
@@ -47,7 +47,7 @@ export { Tooltip } from './Tooltip';
47
47
  export { Typography } from './Typography';
48
48
  export { TabContext, TabList, TabPanel, TreeItem, TreeView } from '@mui/lab';
49
49
  export { CardActionArea, CardActions, CardContent, CardHeader, CardMedia, Input, InputAdornment, InputLabel, ListItemButton, ListItemText, MenuList, SvgIcon, TableBody, TableCell, TableContainer, TableHead, TablePagination, TableRow, TableSortLabel, } from '@mui/material';
50
- export { GridToolbarContainer, GridToolbarQuickFilter, useGridApiRef } from '@mui/x-data-grid';
50
+ export { useGridApiRef } from '@mui/x-data-grid';
51
51
  export { TreeItem as RichTreeItem, RichTreeView, TreeItem2Content, TreeItem2DragAndDropOverlay, TreeItem2GroupTransition, TreeItem2Icon, TreeItem2IconContainer, TreeItem2Label, TreeItem2Provider, TreeItem2Root, useTreeItem2, } from '@mui/x-tree-view';
52
52
  export type { GridSize } from '@mui/material';
53
53
  export type { GridCellParams, GridColDef, GridEventListener, GridFilterModel, GridInitialState, GridRowParams, GridSortModel, GridValueFormatterParams, GridValueGetterParams, } from '@mui/x-data-grid';
@@ -48,5 +48,5 @@ export { Typography } from './Typography';
48
48
  //TODO: Review following components. They also need theme control:
49
49
  export { TabContext, TabList, TabPanel, TreeItem, TreeView } from '@mui/lab';
50
50
  export { CardActionArea, CardActions, CardContent, CardHeader, CardMedia, Input, InputAdornment, InputLabel, ListItemButton, ListItemText, MenuList, SvgIcon, TableBody, TableCell, TableContainer, TableHead, TablePagination, TableRow, TableSortLabel, } from '@mui/material';
51
- export { GridToolbarContainer, GridToolbarQuickFilter, useGridApiRef } from '@mui/x-data-grid';
51
+ export { useGridApiRef } from '@mui/x-data-grid';
52
52
  export { TreeItem as RichTreeItem, RichTreeView, TreeItem2Content, TreeItem2DragAndDropOverlay, TreeItem2GroupTransition, TreeItem2Icon, TreeItem2IconContainer, TreeItem2Label, TreeItem2Provider, TreeItem2Root, useTreeItem2, } from '@mui/x-tree-view';
@@ -17,32 +17,7 @@ const BuilderGrid = (props) => {
17
17
  borderBottom: 'none',
18
18
  boxShadow: 'rgba(145, 158, 171, 0.2) 0px 8px 16px',
19
19
  } },
20
- React.createElement(MuiDataGrid, { autoPageSize: !disablePagination, hideFooterPagination: disablePagination, hideFooter: disablePagination, onMenuOpen: (env) => setAnchorEl(env.target), loading: loading, rows: rows, getRowId: (row) => row.id, disableColumnMenu: true, initialState: {
21
- sorting: {
22
- sortModel: initialSort ? [initialSort] : [],
23
- },
24
- }, componentsProps: {
25
- panel: {
26
- anchorEl: anchorEl,
27
- placement: 'bottom-end',
28
- sx: {
29
- '& .MuiPaper-root': {
30
- borderRadius: '6px',
31
- boxShadow: '0px 24px 48px rgba(145, 158, 171, 0.4)',
32
- padding: '8px',
33
- stop: -120,
34
- },
35
- },
36
- },
37
- }, slots: {
38
- toolbar: hideToolbar ? null : toolbar,
39
- noResultsOverlay: () => {
40
- return React.createElement(Box, null);
41
- },
42
- noRowsOverlay: () => {
43
- return noRowsOverlay ? noRowsOverlay : null;
44
- },
45
- }, rowHeight: 60, columnHeaderHeight: 62, ...rest, sx: {
20
+ React.createElement(MuiDataGrid, { autoPageSize: !disablePagination, hideFooterPagination: disablePagination, hideFooter: disablePagination, onMenuOpen: (env) => setAnchorEl(env.target), loading: loading, rows: rows, ...rest, sx: {
46
21
  border: 'none',
47
22
  '& .MuiDataGrid-toolbarContainer': {
48
23
  padding: '0',
@@ -89,7 +64,32 @@ const BuilderGrid = (props) => {
89
64
  },
90
65
  height: disablePagination && !loading ? 'auto' : 'calc(100vh - 240px)',
91
66
  ...rest.sx,
92
- } }))) : (React.createElement(React.Fragment, null, error ? (React.createElement(Box, { sx: {
67
+ }, getRowId: (row) => row.id, disableColumnMenu: true, initialState: {
68
+ sorting: {
69
+ sortModel: initialSort ? [initialSort] : [],
70
+ },
71
+ }, componentsProps: {
72
+ panel: {
73
+ anchorEl: anchorEl,
74
+ placement: 'bottom-end',
75
+ sx: {
76
+ '& .MuiPaper-root': {
77
+ borderRadius: '6px',
78
+ boxShadow: '0px 24px 48px rgba(145, 158, 171, 0.4)',
79
+ padding: '8px',
80
+ stop: -120,
81
+ },
82
+ },
83
+ },
84
+ }, slots: {
85
+ toolbar: hideToolbar ? null : toolbar,
86
+ noResultsOverlay: () => {
87
+ return React.createElement(Box, null);
88
+ },
89
+ noRowsOverlay: () => {
90
+ return noRowsOverlay ? noRowsOverlay : null;
91
+ },
92
+ }, rowHeight: 60, columnHeaderHeight: 62 }))) : (React.createElement(React.Fragment, null, error ? (React.createElement(Box, { sx: {
93
93
  backgroundColor: '#fff',
94
94
  borderRadius: '6px',
95
95
  display: 'flex',
@@ -7,7 +7,6 @@ import sift from 'sift';
7
7
  import { Edit, TrashCan } from '../../../../../icons';
8
8
  import { Button, IconButton, Skeleton, Snackbar, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Tooltip, Typography, } from '../../../../core';
9
9
  import { Box } from '../../../../layout';
10
- import { getReadableQuery } from '../../../CriteriaBuilder';
11
10
  import { getPrefixedUrl, normalizeDateTime, retrieveCustomErrorMessage } from '../../utils';
12
11
  import { ActionDialog } from './ActionDialog';
13
12
  import { DocumentViewerCell } from './DocumentViewerCell';
@@ -38,7 +37,6 @@ const RepeatableField = (props) => {
38
37
  const [relatedObject, setRelatedObject] = useState();
39
38
  const [hasCreateAction, setHasCreateAction] = useState(false);
40
39
  const [users, setUsers] = useState();
41
- const [criteriaObjects, setCriteriaObjects] = useState([]);
42
40
  const [openDialog, setOpenDialog] = useState(false);
43
41
  const [dialogType, setDialogType] = useState();
44
42
  const [selectedRow, setSelectedRow] = useState();
@@ -52,7 +50,9 @@ const RepeatableField = (props) => {
52
50
  const [error, setError] = useState(false);
53
51
  const DEFAULT_CREATE_ACTION = '_create';
54
52
  const { instanceChanges } = useNotification();
55
- const fetchRelatedObject = useCallback(async () => {
53
+ const fetchRelatedInstances = useCallback(async () => {
54
+ if (openDialog)
55
+ return;
56
56
  let relatedObject;
57
57
  if (property.objectId) {
58
58
  try {
@@ -80,86 +80,31 @@ const RepeatableField = (props) => {
80
80
  catch (err) {
81
81
  console.error(error);
82
82
  }
83
- }
84
- relatedObject && checkCreateAccess(relatedObject);
85
- }, [apiServices, property]);
86
- const fetchRelatedInstances = useCallback(async () => {
87
- if (openDialog)
88
- return;
89
- if (property.objectId && property.relatedPropertyId && instance?.id) {
90
- const filterProperty = `${property.relatedPropertyId}.id`;
91
- const filter = { where: { [filterProperty]: instance?.id }, limit: 100 };
92
- const objectId = property.objectId;
93
- try {
94
- const timeout = setTimeout(() => {
83
+ if (property.relatedPropertyId && instance?.id) {
84
+ const filterProperty = `${property.relatedPropertyId}.id`;
85
+ const filter = { where: { [filterProperty]: instance?.id }, limit: 100 };
86
+ const objectId = property.objectId;
87
+ try {
88
+ const timeout = setTimeout(() => {
89
+ setLoading(false);
90
+ }, 300);
91
+ setLoading(true);
92
+ const instances = await apiServices.get(getPrefixedUrl(`/objects/${objectId}/instances`), {
93
+ params: { filter: JSON.stringify(filter) },
94
+ });
95
+ clearTimeout(timeout);
95
96
  setLoading(false);
96
- }, 300);
97
- setLoading(true);
98
- const instances = await apiServices.get(getPrefixedUrl(`/objects/${objectId}/instances`), {
99
- params: { filter: JSON.stringify(filter) },
100
- });
101
- clearTimeout(timeout);
102
- setLoading(false);
103
- if (instances) {
104
- setRelatedInstances(instances);
97
+ if (instances) {
98
+ setRelatedInstances(instances);
99
+ }
100
+ }
101
+ catch (error) {
102
+ setError(true);
105
103
  }
106
104
  }
107
- catch (error) {
108
- setError(true);
109
- }
110
- }
111
- }, [apiServices, property]);
112
- const fetchCriteriaObjects = useCallback(async () => {
113
- let objectIds = [];
114
- const criteriaProperties = relatedObject?.properties?.filter((property) => property.type === 'criteria' && property.objectId) ?? [];
115
- if (tableViewLayout) {
116
- objectIds = criteriaProperties
117
- .filter((p) => tableViewLayout.properties.some((column) => column.id === p.id))
118
- .map((property) => property.objectId);
119
- }
120
- else {
121
- objectIds = criteriaProperties.map((p) => p.objectId);
122
- }
123
- const objects = [];
124
- for (const objectId of new Set(objectIds)) {
125
- try {
126
- const criteriaObject = await apiServices.get(getPrefixedUrl(`/objects/${objectId}/effective`), {
127
- params: { fields: ['id', 'name', 'properties'] },
128
- });
129
- objects.push(criteriaObject);
130
- }
131
- catch (error) {
132
- console.error(`Error fetching criteria object with ID ${objectId}:`, error);
133
- }
134
- }
135
- setCriteriaObjects(objects);
136
- }, [apiServices, relatedObject, tableViewLayout]);
137
- useEffect(() => {
138
- (async () => {
139
- try {
140
- const users = await apiServices.get(getPrefixedUrl(`/users`));
141
- setUsers(users);
142
- }
143
- catch (error) {
144
- console.error(error);
145
- }
146
- })();
147
- }, [apiServices]);
148
- useEffect(() => {
149
- fetchRelatedObject();
150
- fetchRelatedInstances();
151
- }, [fetchRelatedInstances, fetchRelatedObject, reloadOnErrorTrigger, instance]);
152
- useEffect(() => {
153
- if (relatedObject)
154
- fetchCriteriaObjects();
155
- }, [fetchCriteriaObjects, relatedObject]);
156
- useEffect(() => {
157
- if (relatedObject?.rootObjectId) {
158
- const callback = () => fetchRelatedInstances();
159
- instanceChanges?.subscribe(relatedObject?.rootObjectId, callback);
160
- return () => instanceChanges?.unsubscribe(relatedObject?.rootObjectId, callback);
161
105
  }
162
- }, [instanceChanges, relatedObject]);
106
+ relatedObject && checkCreateAccess(relatedObject);
107
+ }, [apiServices, property, viewLayout]);
163
108
  const retrieveCriteria = (relatedObjProperty, action, object) => {
164
109
  let property;
165
110
  if (action.parameters) {
@@ -226,6 +171,27 @@ const RepeatableField = (props) => {
226
171
  });
227
172
  }
228
173
  };
174
+ useEffect(() => {
175
+ (async () => {
176
+ try {
177
+ const users = await apiServices.get(getPrefixedUrl(`/users`));
178
+ setUsers(users);
179
+ }
180
+ catch (error) {
181
+ console.error(error);
182
+ }
183
+ })();
184
+ }, [apiServices]);
185
+ useEffect(() => {
186
+ fetchRelatedInstances();
187
+ }, [fetchRelatedInstances, reloadOnErrorTrigger, instance]);
188
+ useEffect(() => {
189
+ if (relatedObject?.rootObjectId) {
190
+ const callback = () => fetchRelatedInstances();
191
+ instanceChanges?.subscribe(relatedObject?.rootObjectId, callback);
192
+ return () => instanceChanges?.unsubscribe(relatedObject?.rootObjectId, callback);
193
+ }
194
+ }, [instanceChanges, relatedObject]);
229
195
  const deleteRow = (id) => {
230
196
  setDialogType('delete');
231
197
  setSelectedRow(id);
@@ -368,24 +334,28 @@ const RepeatableField = (props) => {
368
334
  };
369
335
  const getValue = (relatedInstance, propertyId, propertyType) => {
370
336
  const value = get(relatedInstance, propertyId);
337
+ // If the property is not date-like then just return the
338
+ // value found at the given path.
339
+ if (!['date', 'date-time', 'time'].includes(propertyType)) {
340
+ return value;
341
+ }
371
342
  // If the date-like value is empty then there is no need to format.
372
343
  if (!value) {
373
344
  return value;
374
345
  }
346
+ // At this point it has been asserted that there is a value
347
+ // and since the property is date-like the value must be
348
+ // a string.
349
+ const stringValue = value;
375
350
  if (propertyType === 'date') {
376
- return DateTime.fromISO(value).toLocaleString(DateTime.DATE_SHORT);
351
+ return DateTime.fromISO(stringValue).toLocaleString(DateTime.DATE_SHORT);
377
352
  }
378
353
  if (propertyType === 'date-time') {
379
- return DateTime.fromISO(value).toLocaleString(DateTime.DATETIME_SHORT);
354
+ return DateTime.fromISO(stringValue).toLocaleString(DateTime.DATETIME_SHORT);
380
355
  }
381
356
  if (propertyType === 'time') {
382
- return DateTime.fromISO(value).toLocaleString(DateTime.TIME_SIMPLE);
383
- }
384
- if (propertyType === 'criteria' && typeof value === 'object') {
385
- const property = relatedObject?.properties?.find((p) => p.id === propertyId);
386
- return getReadableQuery(value, criteriaObjects.find((o) => o.id === property?.objectId)?.properties ?? []);
357
+ return DateTime.fromISO(stringValue).toLocaleString(DateTime.TIME_SIMPLE);
387
358
  }
388
- return value;
389
359
  };
390
360
  const columns = retrieveViewLayout();
391
361
  return loading ? (React.createElement(React.Fragment, null,
@@ -1,5 +1,4 @@
1
1
  import { createFilterOptions, List, ListSubheader } from '@mui/material';
2
- import { uniq } from 'lodash';
3
2
  import React, { forwardRef, useEffect, useRef, useState } from 'react';
4
3
  import { Clear } from '../../../../icons';
5
4
  import { Autocomplete, FormControl, FormControlLabel, IconButton, Radio, RadioGroup, TextField, Typography, } from '../../../core';
@@ -16,7 +15,7 @@ const Select = (props) => {
16
15
  const [isOtherFocused, setIsOtherFocused] = useState(false);
17
16
  const [value, setValue] = useState(defaultValue);
18
17
  const [inputValue, setInputValue] = useState(!selectOptions?.some((option) => (typeof option === 'string' && option === defaultValue) ||
19
- option.value === defaultValue) && property.type !== 'array'
18
+ option.value === defaultValue)
20
19
  ? defaultValue
21
20
  : '');
22
21
  const [errorState, setErrorState] = useState();
@@ -32,7 +31,7 @@ const Select = (props) => {
32
31
  const handleChange = (event, selected) => {
33
32
  if (Array.isArray(selected)) {
34
33
  const newValues = selected.map((option) => option.value ?? option);
35
- setValue(uniq(newValues));
34
+ setValue(newValues);
36
35
  onChange && onChange(property.id, newValues, property);
37
36
  }
38
37
  else {
@@ -145,7 +144,6 @@ const Select = (props) => {
145
144
  filtered.push({
146
145
  value: inputValue,
147
146
  label: `Add "${inputValue}"`,
148
- isCustomValue: true,
149
147
  });
150
148
  }
151
149
  return filtered;
@@ -153,16 +151,9 @@ const Select = (props) => {
153
151
  ? (option, value) => isOptionEqualToValue(option, value)
154
152
  : undefined, getOptionLabel: getOptionLabel && !isCombobox
155
153
  ? (option) => getOptionLabel(option)
156
- : (option) => {
157
- if (typeof option === 'string')
158
- return option;
159
- // If the option is a custom value, return the value without the prepended "Add" text.
160
- if (option.isCustomValue)
161
- return option.value;
162
- return option.label ?? '';
163
- }, renderOption: renderOption
154
+ : (option) => (typeof option === 'string' ? option : option.label), renderOption: renderOption
164
155
  ? (props, option, state) => renderOption(props, option, state)
165
- : (props, option) => React.createElement("li", { ...props }, option.label ?? option), ListboxComponent: ListboxComponent, disableCloseOnSelect: disableCloseOnSelect, sx: {
156
+ : undefined, ListboxComponent: ListboxComponent, disableCloseOnSelect: disableCloseOnSelect, sx: {
166
157
  '& button.MuiButtonBase-root': {
167
158
  visibility: 'visible',
168
159
  },
@@ -1,4 +1,4 @@
1
- export { ClickAwayListener, createTheme, darken, lighten, styled, Toolbar, useMediaQuery, useTheme, } from '@mui/material';
1
+ export { ClickAwayListener, createTheme, darken, lighten, styled, Toolbar } from '@mui/material';
2
2
  export { CalendarPicker, DateTimePicker, MonthPicker, PickersDay, StaticDateTimePicker, StaticTimePicker, TimePicker, YearPicker, } from '@mui/x-date-pickers';
3
3
  export * from './colors';
4
4
  export * from './components/core';
@@ -1,4 +1,4 @@
1
- export { ClickAwayListener, createTheme, darken, lighten, styled, Toolbar, useMediaQuery, useTheme, } from '@mui/material';
1
+ export { ClickAwayListener, createTheme, darken, lighten, styled, Toolbar } from '@mui/material';
2
2
  export { CalendarPicker, DateTimePicker, MonthPicker, PickersDay, StaticDateTimePicker, StaticTimePicker, TimePicker, YearPicker, } from '@mui/x-date-pickers';
3
3
  export * from './colors';
4
4
  export * from './components/core';
@@ -29,81 +29,3 @@ export declare function useResponsive(): {
29
29
  /** Returns true if viewport width is between the specified breakpoints */
30
30
  between: (start: Breakpoint, end: Breakpoint) => boolean;
31
31
  };
32
- export interface WidgetSizeInfo {
33
- /** The ref to attach to the widget container */
34
- ref: (element: HTMLElement | SVGElement | null) => void;
35
- /** Width in pixels */
36
- width: number;
37
- /** Height in pixels */
38
- height: number;
39
- /** Widget size breakpoints */
40
- breakpoints: {
41
- /** Extra small: width < 800px */
42
- isXs: boolean;
43
- /** Small: 800px <= width < 1000px */
44
- isSm: boolean;
45
- /** Medium: 1000px <= width < 1200px */
46
- isMd: boolean;
47
- /** Large: 1200px <= width < 1400px */
48
- isLg: boolean;
49
- /** Extra large: width >= 1400px */
50
- isXl: boolean;
51
- /** Compact height: height < 300px */
52
- isCompact: boolean;
53
- /** Tall height: height >= 600px */
54
- isTall: boolean;
55
- };
56
- /** Bounds information from react-use-measure */
57
- bounds: {
58
- /**
59
- * left is usually the same as x, but can differ if width is negative.
60
- * See: https://developer.mozilla.org/en-US/docs/Web/API/DOMRect
61
- */
62
- left: number;
63
- /**
64
- * top is usually the same as y, but can differ if height is negative.
65
- * See: https://developer.mozilla.org/en-US/docs/Web/API/DOMRect
66
- */
67
- top: number;
68
- width: number;
69
- height: number;
70
- bottom: number;
71
- right: number;
72
- x: number;
73
- y: number;
74
- };
75
- /** Check if widget is above a breakpoint */
76
- isAbove: (breakpoint: 'xs' | 'sm' | 'md' | 'lg' | 'xl') => boolean;
77
- /** Check if widget is below a breakpoint */
78
- isBelow: (breakpoint: 'xs' | 'sm' | 'md' | 'lg' | 'xl') => boolean;
79
- }
80
- /**
81
- * Custom hook that measures widget dimensions and provides responsive breakpoints
82
- * based on the actual widget size rather than viewport size.
83
- *
84
- * @param options Configuration options for the measurement
85
- * @returns WidgetSizeInfo object with ref, dimensions, and breakpoints
86
- *
87
- * @example
88
- * ```tsx
89
- * const { ref, width, breakpoints } = useWidgetSize();
90
- *
91
- * return (
92
- * <div ref={ref}>
93
- * {breakpoints.isXs ? 'Compact view' : 'Full view'}
94
- * <p>Widget width: {width}px</p>
95
- * </div>
96
- * );
97
- * ```
98
- */
99
- export declare const useWidgetSize: (options?: {
100
- /** Debounce measurement updates (ms) */
101
- debounce?: number;
102
- /** Throttle measurement updates (ms) */
103
- throttle?: number;
104
- /** Scroll containers to observe */
105
- scroll?: boolean;
106
- /** Resize containers to observe */
107
- resize?: boolean;
108
- }) => WidgetSizeInfo;
109
- export default useWidgetSize;
@@ -1,6 +1,5 @@
1
1
  import { useTheme } from '@mui/material';
2
2
  import useMediaQuery from '@mui/material/useMediaQuery';
3
- import useMeasure from 'react-use-measure';
4
3
  /**
5
4
  * Custom hook for responsive design breakpoints using size terminology.
6
5
  * Breakpoints based on MUI default theme:
@@ -34,67 +33,3 @@ export function useResponsive() {
34
33
  between: (start, end) => useMediaQuery(theme.breakpoints.between(start, end)),
35
34
  };
36
35
  }
37
- /**
38
- * Custom hook that measures widget dimensions and provides responsive breakpoints
39
- * based on the actual widget size rather than viewport size.
40
- *
41
- * @param options Configuration options for the measurement
42
- * @returns WidgetSizeInfo object with ref, dimensions, and breakpoints
43
- *
44
- * @example
45
- * ```tsx
46
- * const { ref, width, breakpoints } = useWidgetSize();
47
- *
48
- * return (
49
- * <div ref={ref}>
50
- * {breakpoints.isXs ? 'Compact view' : 'Full view'}
51
- * <p>Widget width: {width}px</p>
52
- * </div>
53
- * );
54
- * ```
55
- */
56
- export const useWidgetSize = (options) => {
57
- const [ref, bounds] = useMeasure({
58
- debounce: options?.debounce ?? 16,
59
- scroll: options?.scroll ?? true,
60
- offsetSize: true,
61
- ...options,
62
- });
63
- const width = bounds.width || 0;
64
- const height = bounds.height || 0;
65
- // Widget-specific breakpoints based on container width
66
- const breakpoints = {
67
- isXs: width < 800,
68
- isSm: width >= 800 && width < 1000,
69
- isMd: width >= 1000 && width < 1200,
70
- isLg: width >= 1200 && width < 1400,
71
- isXl: width >= 1400,
72
- isCompact: height < 300,
73
- isTall: height >= 600,
74
- };
75
- // Breakpoint thresholds
76
- const breakpointThresholds = {
77
- xs: 800,
78
- sm: 1000,
79
- md: 1200,
80
- lg: 1400,
81
- xl: Infinity,
82
- };
83
- // Utility functions for responsive checks
84
- const isAbove = (breakpoint) => {
85
- return width >= breakpointThresholds[breakpoint];
86
- };
87
- const isBelow = (breakpoint) => {
88
- return width < breakpointThresholds[breakpoint];
89
- };
90
- return {
91
- ref,
92
- width,
93
- height,
94
- breakpoints,
95
- bounds,
96
- isAbove,
97
- isBelow,
98
- };
99
- };
100
- export default useWidgetSize;
@@ -1,5 +1,5 @@
1
1
  import { useMediaQuery, useTheme } from '@mui/material';
2
2
  import UIThemeProvider from './UIThemeProvider';
3
- import { useResponsive, useWidgetSize, type WidgetSizeInfo } from './hooks';
4
- export { useMediaQuery, useResponsive, useTheme, useWidgetSize, type WidgetSizeInfo };
3
+ import { useResponsive } from './hooks';
4
+ export { useMediaQuery, useResponsive, useTheme };
5
5
  export default UIThemeProvider;
@@ -1,5 +1,5 @@
1
1
  import { useMediaQuery, useTheme } from '@mui/material';
2
2
  import UIThemeProvider from './UIThemeProvider';
3
- import { useResponsive, useWidgetSize } from './hooks';
4
- export { useMediaQuery, useResponsive, useTheme, useWidgetSize };
3
+ import { useResponsive } from './hooks';
4
+ export { useMediaQuery, useResponsive, useTheme };
5
5
  export default UIThemeProvider;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evoke-platform/ui-components",
3
- "version": "1.6.0-dev.6",
3
+ "version": "1.6.0-testing.0",
4
4
  "description": "",
5
5
  "main": "dist/published/index.js",
6
6
  "module": "dist/published/index.js",
@@ -128,7 +128,6 @@
128
128
  "react-input-mask": "^2.0.4",
129
129
  "react-number-format": "^4.9.3",
130
130
  "react-querybuilder": "^6.0.2",
131
- "react-use-measure": "^2.1.7",
132
131
  "rtf.js": "^3.0.9",
133
132
  "sift": "^17.1.3",
134
133
  "small-date": "^2.0.0",
@@ -1 +0,0 @@
1
- export {};
@@ -1,187 +0,0 @@
1
- import { renderHook } from '@testing-library/react';
2
- import { beforeEach, describe, expect, it, vi } from 'vitest';
3
- import { useWidgetSize } from '.';
4
- // Shared mock bounds for useMeasure
5
- let mockBounds = {
6
- left: 0,
7
- top: 0,
8
- width: 0,
9
- height: 0,
10
- bottom: 0,
11
- right: 0,
12
- x: 0,
13
- y: 0,
14
- };
15
- function setMockBounds(newBounds) {
16
- mockBounds = { ...mockBounds, ...newBounds };
17
- }
18
- // Mock useMeasure from react-use-measure
19
- vi.mock('react-use-measure', () => {
20
- const useMeasure = () => [
21
- vi.fn(),
22
- mockBounds,
23
- ];
24
- return { __esModule: true, default: useMeasure, useMeasure };
25
- });
26
- describe('useWidgetSize', () => {
27
- beforeEach(() => {
28
- // Reset bounds before each test
29
- setMockBounds({
30
- left: 0,
31
- top: 0,
32
- width: 0,
33
- height: 0,
34
- bottom: 0,
35
- right: 0,
36
- x: 0,
37
- y: 0,
38
- });
39
- });
40
- it('should provide ref and basic structure', () => {
41
- setMockBounds({ width: 123, height: 456 });
42
- const { result } = renderHook(() => useWidgetSize());
43
- expect(result.current.ref).toBeDefined();
44
- expect(typeof result.current.ref).toBe('function');
45
- expect(result.current.width).toBe(123);
46
- expect(result.current.height).toBe(456);
47
- });
48
- describe('breakpoints', () => {
49
- it('should set isXs for width < 800', () => {
50
- setMockBounds({ width: 799, height: 100 });
51
- const { result } = renderHook(() => useWidgetSize());
52
- expect(result.current.breakpoints.isXs).toBe(true);
53
- expect(result.current.breakpoints.isSm).toBe(false);
54
- expect(result.current.breakpoints.isMd).toBe(false);
55
- expect(result.current.breakpoints.isLg).toBe(false);
56
- expect(result.current.breakpoints.isXl).toBe(false);
57
- });
58
- it('should set isSm for 800 <= width < 1000', () => {
59
- setMockBounds({ width: 800, height: 100 });
60
- let { result } = renderHook(() => useWidgetSize());
61
- expect(result.current.breakpoints.isXs).toBe(false);
62
- expect(result.current.breakpoints.isSm).toBe(true);
63
- expect(result.current.breakpoints.isMd).toBe(false);
64
- expect(result.current.breakpoints.isLg).toBe(false);
65
- expect(result.current.breakpoints.isXl).toBe(false);
66
- setMockBounds({ width: 999, height: 100 });
67
- result = renderHook(() => useWidgetSize()).result;
68
- expect(result.current.breakpoints.isXs).toBe(false);
69
- expect(result.current.breakpoints.isSm).toBe(true);
70
- expect(result.current.breakpoints.isMd).toBe(false);
71
- expect(result.current.breakpoints.isLg).toBe(false);
72
- expect(result.current.breakpoints.isXl).toBe(false);
73
- });
74
- it('should set isMd for 1000 <= width < 1200', () => {
75
- setMockBounds({ width: 1000, height: 100 });
76
- let { result } = renderHook(() => useWidgetSize());
77
- expect(result.current.breakpoints.isXs).toBe(false);
78
- expect(result.current.breakpoints.isSm).toBe(false);
79
- expect(result.current.breakpoints.isMd).toBe(true);
80
- expect(result.current.breakpoints.isLg).toBe(false);
81
- expect(result.current.breakpoints.isXl).toBe(false);
82
- setMockBounds({ width: 1199, height: 100 });
83
- result = renderHook(() => useWidgetSize()).result;
84
- expect(result.current.breakpoints.isXs).toBe(false);
85
- expect(result.current.breakpoints.isSm).toBe(false);
86
- expect(result.current.breakpoints.isMd).toBe(true);
87
- expect(result.current.breakpoints.isLg).toBe(false);
88
- expect(result.current.breakpoints.isXl).toBe(false);
89
- });
90
- it('should set isLg for 1200 <= width < 1400', () => {
91
- setMockBounds({ width: 1200, height: 100 });
92
- let { result } = renderHook(() => useWidgetSize());
93
- expect(result.current.breakpoints.isXs).toBe(false);
94
- expect(result.current.breakpoints.isSm).toBe(false);
95
- expect(result.current.breakpoints.isMd).toBe(false);
96
- expect(result.current.breakpoints.isLg).toBe(true);
97
- expect(result.current.breakpoints.isXl).toBe(false);
98
- setMockBounds({ width: 1399, height: 100 });
99
- result = renderHook(() => useWidgetSize()).result;
100
- expect(result.current.breakpoints.isXs).toBe(false);
101
- expect(result.current.breakpoints.isSm).toBe(false);
102
- expect(result.current.breakpoints.isMd).toBe(false);
103
- expect(result.current.breakpoints.isLg).toBe(true);
104
- expect(result.current.breakpoints.isXl).toBe(false);
105
- });
106
- it('should set isXl for width >= 1400', () => {
107
- setMockBounds({ width: 1400, height: 100 });
108
- let { result } = renderHook(() => useWidgetSize());
109
- expect(result.current.breakpoints.isXs).toBe(false);
110
- expect(result.current.breakpoints.isSm).toBe(false);
111
- expect(result.current.breakpoints.isMd).toBe(false);
112
- expect(result.current.breakpoints.isLg).toBe(false);
113
- expect(result.current.breakpoints.isXl).toBe(true);
114
- setMockBounds({ width: 2000, height: 100 });
115
- result = renderHook(() => useWidgetSize()).result;
116
- expect(result.current.breakpoints.isXs).toBe(false);
117
- expect(result.current.breakpoints.isSm).toBe(false);
118
- expect(result.current.breakpoints.isMd).toBe(false);
119
- expect(result.current.breakpoints.isLg).toBe(false);
120
- expect(result.current.breakpoints.isXl).toBe(true);
121
- });
122
- it('should set isCompact for height < 300', () => {
123
- setMockBounds({ width: 1000, height: 299 });
124
- const { result } = renderHook(() => useWidgetSize());
125
- expect(result.current.breakpoints.isCompact).toBe(true);
126
- expect(result.current.breakpoints.isTall).toBe(false);
127
- });
128
- it('should set isTall for height >= 600', () => {
129
- setMockBounds({ width: 1000, height: 600 });
130
- const { result } = renderHook(() => useWidgetSize());
131
- expect(result.current.breakpoints.isTall).toBe(true);
132
- expect(result.current.breakpoints.isCompact).toBe(false);
133
- setMockBounds({ width: 1000, height: 800 });
134
- const { result: result2 } = renderHook(() => useWidgetSize());
135
- expect(result2.current.breakpoints.isTall).toBe(true);
136
- expect(result2.current.breakpoints.isCompact).toBe(false);
137
- });
138
- it('should not set isCompact or isTall for 300 <= height < 600', () => {
139
- setMockBounds({ width: 1000, height: 300 });
140
- let { result } = renderHook(() => useWidgetSize());
141
- expect(result.current.breakpoints.isCompact).toBe(false);
142
- expect(result.current.breakpoints.isTall).toBe(false);
143
- setMockBounds({ width: 1000, height: 599 });
144
- result = renderHook(() => useWidgetSize()).result;
145
- expect(result.current.breakpoints.isCompact).toBe(false);
146
- expect(result.current.breakpoints.isTall).toBe(false);
147
- });
148
- });
149
- it('should provide bounds information', () => {
150
- setMockBounds({
151
- left: 1,
152
- top: 2,
153
- width: 300,
154
- height: 400,
155
- bottom: 402,
156
- right: 301,
157
- x: 1,
158
- y: 2,
159
- });
160
- const { result } = renderHook(() => useWidgetSize());
161
- expect(result.current.bounds).toBeDefined();
162
- expect(result.current.bounds.left).toBe(1);
163
- expect(result.current.bounds.top).toBe(2);
164
- expect(result.current.bounds.width).toBe(300);
165
- expect(result.current.bounds.height).toBe(400);
166
- expect(result.current.bounds.bottom).toBe(402);
167
- expect(result.current.bounds.right).toBe(301);
168
- expect(result.current.bounds.x).toBe(1);
169
- expect(result.current.bounds.y).toBe(2);
170
- });
171
- it('should provide responsive utility functions', () => {
172
- setMockBounds({ width: 1200 });
173
- const { result } = renderHook(() => useWidgetSize());
174
- expect(result.current.isAbove).toBeDefined();
175
- expect(typeof result.current.isAbove).toBe('function');
176
- expect(result.current.isBelow).toBeDefined();
177
- expect(typeof result.current.isBelow).toBe('function');
178
- });
179
- it('should calculate responsive utility functions correctly', () => {
180
- setMockBounds({ width: 1100 });
181
- const { result } = renderHook(() => useWidgetSize());
182
- expect(result.current.isAbove('sm')).toBe(true); // 1100 >= 1000
183
- expect(result.current.isBelow('lg')).toBe(true); // 1100 < 1400
184
- expect(result.current.isAbove('xl')).toBe(false); // 1100 < Infinity
185
- expect(result.current.isBelow('xs')).toBe(false); // 1100 >= 800
186
- });
187
- });