@zesty-io/material 0.1.0 → 0.1.3

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 (65) hide show
  1. package/es/ConfirmDialog/ConfirmDialog.stories.d.ts +6 -0
  2. package/es/ConfirmDialog/ConfirmDialog.stories.js +24 -0
  3. package/es/ConfirmDialog/index.d.ts +18 -0
  4. package/es/ConfirmDialog/index.js +11 -0
  5. package/es/CopyButton/index.js +3 -3
  6. package/es/FieldTypeColor/index.d.ts +1 -1
  7. package/es/FieldTypeColor/index.js +12 -16
  8. package/es/FieldTypeDate/FieldTypeDate.stories.js +1 -4
  9. package/es/FieldTypeDate/index.d.ts +4 -6
  10. package/es/FieldTypeDate/index.js +4 -8
  11. package/es/FieldTypeDateTime/FieldTypeDateTime.stories.js +1 -4
  12. package/es/FieldTypeDateTime/index.d.ts +4 -6
  13. package/es/FieldTypeDateTime/index.js +4 -8
  14. package/es/FieldTypeDropdown/FieldTypeDropwdon.stories.js +2 -1
  15. package/es/FieldTypeDropdown/index.d.ts +1 -1
  16. package/es/FieldTypeDropdown/index.js +5 -10
  17. package/es/FieldTypeNumber/FieldTypeNumber.stories.js +1 -0
  18. package/es/FieldTypeNumber/index.d.ts +1 -1
  19. package/es/FieldTypeNumber/index.js +4 -7
  20. package/es/FieldTypeOneToMany/FieldTypeOneToMany.stories.d.ts +5 -0
  21. package/es/FieldTypeOneToMany/FieldTypeOneToMany.stories.js +28 -0
  22. package/es/FieldTypeOneToMany/index.d.ts +32 -0
  23. package/es/FieldTypeOneToMany/index.js +29 -0
  24. package/es/FieldTypeOneToOne/FieldTypeOneToOne.stories.d.ts +5 -0
  25. package/es/FieldTypeOneToOne/FieldTypeOneToOne.stories.js +28 -0
  26. package/es/FieldTypeOneToOne/index.d.ts +34 -0
  27. package/es/FieldTypeOneToOne/index.js +33 -0
  28. package/es/FieldTypeSort/FieldTypeSort.stories.js +1 -0
  29. package/es/FieldTypeSort/index.d.ts +1 -1
  30. package/es/FieldTypeSort/index.js +17 -27
  31. package/es/FieldTypeText/FieldTypeText.stories.js +4 -2
  32. package/es/FieldTypeText/index.d.ts +1 -1
  33. package/es/FieldTypeText/index.js +4 -12
  34. package/es/FieldTypeUrl/index.d.ts +1 -1
  35. package/es/FieldTypeUrl/index.js +8 -16
  36. package/es/index.d.ts +3 -1
  37. package/es/index.js +3 -1
  38. package/es/theme/index.js +10 -0
  39. package/es/utils/virtualization.d.ts +2 -0
  40. package/es/utils/virtualization.js +63 -0
  41. package/package.json +5 -3
  42. package/src/{ConfirmModal/ConfirmModal.stories.tsx → ConfirmDialog/ConfirmDialog.stories.tsx} +0 -0
  43. package/src/{ConfirmModal → ConfirmDialog}/index.tsx +0 -0
  44. package/src/CopyButton/index.tsx +6 -6
  45. package/src/FieldTypeColor/index.tsx +5 -7
  46. package/src/FieldTypeDate/FieldTypeDate.stories.tsx +1 -4
  47. package/src/FieldTypeDate/index.tsx +22 -25
  48. package/src/FieldTypeDateTime/FieldTypeDateTime.stories.tsx +1 -4
  49. package/src/FieldTypeDateTime/index.tsx +23 -25
  50. package/src/FieldTypeDropdown/FieldTypeDropwdon.stories.tsx +2 -1
  51. package/src/FieldTypeDropdown/index.tsx +5 -7
  52. package/src/FieldTypeNumber/FieldTypeNumber.stories.tsx +1 -0
  53. package/src/FieldTypeNumber/index.tsx +5 -6
  54. package/src/FieldTypeOneToMany/FieldTypeOneToMany.stories.tsx +47 -0
  55. package/src/FieldTypeOneToMany/index.tsx +90 -0
  56. package/src/FieldTypeOneToOne/FieldTypeOneToOne.stories.tsx +46 -0
  57. package/src/FieldTypeOneToOne/index.tsx +96 -0
  58. package/src/FieldTypeSort/FieldTypeSort.stories.tsx +1 -0
  59. package/src/FieldTypeSort/index.tsx +9 -18
  60. package/src/FieldTypeText/FieldTypeText.stories.tsx +9 -5
  61. package/src/FieldTypeText/index.tsx +11 -18
  62. package/src/FieldTypeUrl/index.tsx +11 -18
  63. package/src/index.ts +3 -1
  64. package/src/theme/index.ts +10 -0
  65. package/src/utils/virtualization.tsx +107 -0
@@ -1,15 +1,7 @@
1
- import { Fragment as _Fragment, jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import MuiTextField from '@mui/material/TextField';
3
- import { InputAdornment } from '@mui/material';
4
- const FieldTypeText = ({ maxLength = 150, value, helperText, InputProps, InputLabelProps, ...props }) => {
5
- return (_jsx(MuiTextField, { size: "small", variant: 'outlined', value: value, InputProps: {
6
- endAdornment: (_jsx(InputAdornment, { position: "end", children: _jsxs(_Fragment, { children: [value.length, "/", maxLength] }) })),
7
- // Spread props at the end to allow Input prop overrides
8
- ...InputProps,
9
- }, InputLabelProps: {
10
- shrink: true,
11
- // Spread props at the end to allow InputLabel prop overrides
12
- ...InputLabelProps,
13
- }, error: value.length > maxLength, helperText: value.length > maxLength ? 'Your input is over the specified limit' : helperText, ...props }));
3
+ import { FormControl, FormLabel, Box } from '@mui/material';
4
+ const FieldTypeText = ({ label, maxLength = 150, value, helperText, required, ...props }) => {
5
+ return (_jsxs(FormControl, { fullWidth: true, required: required, children: [_jsxs(FormLabel, { sx: { display: 'flex', justifyContent: 'space-between', '& .MuiFormLabel-asterisk': { order: 2 } }, children: [_jsx(Box, { sx: { order: 1 }, children: label }), _jsxs(Box, { sx: { order: 3, flex: 1, textAlign: 'right' }, children: [value?.length, "/", maxLength] })] }), _jsx(MuiTextField, { size: "small", variant: 'outlined', value: value, error: value?.length > maxLength, helperText: value?.length > maxLength ? 'Your input is over the specified limit' : helperText, ...props })] }));
14
6
  };
15
7
  export default FieldTypeText;
@@ -8,5 +8,5 @@ export interface FieldTypeUrlProps extends Omit<OutlinedTextFieldProps, 'variant
8
8
  maxLength?: number;
9
9
  value: string;
10
10
  }
11
- declare const FieldTypeUrl: ({ maxLength, value, helperText, InputProps, InputLabelProps, inputProps, ...props }: FieldTypeUrlProps) => JSX.Element;
11
+ declare const FieldTypeUrl: ({ label, maxLength, value, helperText, required, inputProps, ...props }: FieldTypeUrlProps) => JSX.Element;
12
12
  export default FieldTypeUrl;
@@ -1,21 +1,13 @@
1
- import { Fragment as _Fragment, jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useRef } from 'react';
3
3
  import MuiTextField from '@mui/material/TextField';
4
- import { InputAdornment } from '@mui/material';
5
- const FieldTypeUrl = ({ maxLength = 2000, value, helperText, InputProps, InputLabelProps, inputProps, ...props }) => {
4
+ import { FormControl, FormLabel, Box } from '@mui/material';
5
+ const FieldTypeUrl = ({ label, maxLength = 2000, value, helperText, required, inputProps, ...props }) => {
6
6
  const inputRef = useRef(null);
7
- return (_jsx(MuiTextField, { size: "small", type: 'url', variant: 'outlined', value: value, InputProps: {
8
- endAdornment: (_jsx(InputAdornment, { position: "end", children: _jsxs(_Fragment, { children: [value.length, "/", maxLength] }) })),
9
- // Spread props at the end to allow Input prop overrides
10
- ...InputProps,
11
- }, InputLabelProps: {
12
- shrink: true,
13
- // Spread props at the end to allow InputLabel prop overrides
14
- ...InputLabelProps,
15
- }, inputProps: {
16
- ref: inputRef,
17
- // Spread props at the end to allow inputProps prop overrides
18
- ...inputProps,
19
- }, error: (value && !inputRef.current?.validity.valid) || value.length > maxLength, helperText: value.length > maxLength ? 'Your input is over the specified limit' : (value && !inputRef.current?.validity.valid) ? 'Your input is not a valid url' : helperText, ...props }));
7
+ return (_jsxs(FormControl, { fullWidth: true, required: required, children: [_jsxs(FormLabel, { sx: { display: 'flex', justifyContent: 'space-between', '& .MuiFormLabel-asterisk': { order: 2 } }, children: [_jsx(Box, { sx: { order: 1 }, children: label }), _jsxs(Box, { sx: { order: 3, flex: 1, textAlign: 'right' }, children: [value?.length, "/", maxLength] })] }), _jsx(MuiTextField, { size: "small", type: 'url', variant: 'outlined', value: value, inputProps: {
8
+ ref: inputRef,
9
+ // Spread props at the end to allow inputProps prop overrides
10
+ ...inputProps,
11
+ }, error: (value && !inputRef.current?.validity.valid) || value?.length > maxLength, helperText: value?.length > maxLength ? 'Your input is over the specified limit' : (value && !inputRef.current?.validity.valid) ? 'Your input is not a valid url' : helperText, ...props })] }));
20
12
  };
21
13
  export default FieldTypeUrl;
package/es/index.d.ts CHANGED
@@ -7,5 +7,7 @@ export { default as FieldTypeDateTime } from './FieldTypeDateTime';
7
7
  export { default as FieldTypeColor } from './FieldTypeColor';
8
8
  export { default as FieldTypeNumber } from './FieldTypeNumber';
9
9
  export { default as FieldTypeDropdown } from './FieldTypeDropdown';
10
+ export { default as FieldTypeOneToOne } from './FieldTypeOneToOne';
11
+ export { default as FieldTypeOneToMany } from './FieldTypeOneToMany';
10
12
  export { default as CopyButton } from './CopyButton';
11
- export { default as ConfirmModal } from './ConfirmModal';
13
+ export { default as ConfirmDialog } from './ConfirmDialog';
package/es/index.js CHANGED
@@ -7,5 +7,7 @@ export { default as FieldTypeDateTime } from './FieldTypeDateTime';
7
7
  export { default as FieldTypeColor } from './FieldTypeColor';
8
8
  export { default as FieldTypeNumber } from './FieldTypeNumber';
9
9
  export { default as FieldTypeDropdown } from './FieldTypeDropdown';
10
+ export { default as FieldTypeOneToOne } from './FieldTypeOneToOne';
11
+ export { default as FieldTypeOneToMany } from './FieldTypeOneToMany';
10
12
  export { default as CopyButton } from './CopyButton';
11
- export { default as ConfirmModal } from './ConfirmModal';
13
+ export { default as ConfirmDialog } from './ConfirmDialog';
package/es/theme/index.js CHANGED
@@ -39,6 +39,16 @@ theme = createTheme(theme, {
39
39
  root: {
40
40
  color: theme.palette.primary.dark,
41
41
  },
42
+ asterisk: {
43
+ color: theme.palette.error.main,
44
+ },
45
+ },
46
+ },
47
+ MuiCardHeader: {
48
+ styleOverrides: {
49
+ root: {
50
+ backgroundColor: '#e4e9f1',
51
+ },
42
52
  },
43
53
  },
44
54
  MuiToggleButton: {
@@ -0,0 +1,2 @@
1
+ import * as React from 'react';
2
+ export declare const ListboxComponent: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLElement> & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,63 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from 'react';
3
+ import useMediaQuery from '@mui/material/useMediaQuery';
4
+ import ListSubheader from '@mui/material/ListSubheader';
5
+ import { useTheme } from '@mui/material/styles';
6
+ import { VariableSizeList } from 'react-window';
7
+ import Typography from '@mui/material/Typography';
8
+ const LISTBOX_PADDING = 8; // px
9
+ function renderRow(props) {
10
+ const { data, index, style } = props;
11
+ const dataSet = data[index];
12
+ const inlineStyle = {
13
+ ...style,
14
+ top: style.top + LISTBOX_PADDING,
15
+ };
16
+ if (dataSet.hasOwnProperty('group')) {
17
+ return (_jsx(ListSubheader, { component: "div", style: inlineStyle, children: dataSet.group }, dataSet.key));
18
+ }
19
+ return (_jsx(Typography, { component: "li", ...dataSet[0], noWrap: true, style: inlineStyle, children: dataSet[1] }));
20
+ }
21
+ const OuterElementContext = React.createContext({});
22
+ const OuterElementType = React.forwardRef((props, ref) => {
23
+ const outerProps = React.useContext(OuterElementContext);
24
+ return _jsx("div", { ref: ref, ...props, ...outerProps });
25
+ });
26
+ function useResetCache(data) {
27
+ const ref = React.useRef(null);
28
+ React.useEffect(() => {
29
+ if (ref.current != null) {
30
+ ref.current.resetAfterIndex(0, true);
31
+ }
32
+ }, [data]);
33
+ return ref;
34
+ }
35
+ // Adapter for react-window
36
+ export const ListboxComponent = React.forwardRef(function ListboxComponent(props, ref) {
37
+ const { children, ...other } = props;
38
+ const itemData = [];
39
+ children.forEach((item) => {
40
+ itemData.push(item);
41
+ itemData.push(...(item.children || []));
42
+ });
43
+ const theme = useTheme();
44
+ const smUp = useMediaQuery(theme.breakpoints.up('sm'), {
45
+ noSsr: true,
46
+ });
47
+ const itemCount = itemData.length;
48
+ const itemSize = smUp ? 36 : 48;
49
+ const getChildSize = (child) => {
50
+ if (child.hasOwnProperty('group')) {
51
+ return 48;
52
+ }
53
+ return itemSize;
54
+ };
55
+ const getHeight = () => {
56
+ if (itemCount > 8) {
57
+ return 8 * itemSize;
58
+ }
59
+ return itemData.map(getChildSize).reduce((a, b) => a + b, 0);
60
+ };
61
+ const gridRef = useResetCache(itemCount);
62
+ return (_jsx("div", { ref: ref, children: _jsx(OuterElementContext.Provider, { value: other, children: _jsx(VariableSizeList, { itemData: itemData, height: getHeight() + 2 * LISTBOX_PADDING, width: "100%", ref: gridRef, outerElementType: OuterElementType, innerElementType: "ul", itemSize: (index) => getChildSize(itemData[index]), overscanCount: 5, itemCount: itemCount, children: renderRow }) }) }));
63
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zesty-io/material",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "description": "Contains custom components which are in addition to the @mui design-system",
5
5
  "author": "Zesty.io",
6
6
  "license": "MIT",
@@ -16,7 +16,7 @@
16
16
  "module": "es/index.js",
17
17
  "types": "es/index.d.ts",
18
18
  "scripts": {
19
- "build": "tsc",
19
+ "build": "npm ci && tsc",
20
20
  "prerelease": "npm test",
21
21
  "release": "npm run build && npm publish --access public",
22
22
  "release:alpha": "npm run build && npm publish --access public --tag alpha",
@@ -32,7 +32,8 @@
32
32
  "@mui/icons-material": "^5.6.2",
33
33
  "@mui/material": "^5.6.4",
34
34
  "@mui/x-date-pickers": "^5.0.0-alpha.5",
35
- "date-fns": "^2.28.0"
35
+ "date-fns": "^2.28.0",
36
+ "react-window": "^1.8.7"
36
37
  },
37
38
  "devDependencies": {
38
39
  "@babel/core": "^7.17.10",
@@ -42,6 +43,7 @@
42
43
  "@storybook/addon-links": "^6.4.22",
43
44
  "@storybook/react": "^6.4.22",
44
45
  "@storybook/testing-library": "0.0.11",
46
+ "@types/react-window": "^1.8.5",
45
47
  "babel-loader": "^8.2.5",
46
48
  "gh-pages": "^3.2.3",
47
49
  "react": "^18.1.0",
File without changes
@@ -39,15 +39,15 @@ const CopyButton = ({value, sx, ...props }: CopyButtonProps) => {
39
39
  // Spread sx prop at the end to allow sx prop overrides
40
40
  ...sx,
41
41
  }}
42
+ startIcon={copied ? (
43
+ <CheckIcon color='success' fontSize='small' />
44
+ ) : (
45
+ <ContentCopyIcon fontSize='small' />
46
+ )}
42
47
  // Spread props at the end to allow prop overrides
43
48
  {...props}
44
49
  >
45
- {copied ? (
46
- <CheckIcon color='success' fontSize='small' />
47
- ) : (
48
- <ContentCopyIcon fontSize='small' />
49
- )}
50
- {props.children ? props.children : value}
50
+ {props.children ? props.children : value}
51
51
  </Button>
52
52
  );
53
53
  };
@@ -1,13 +1,15 @@
1
1
  import { useRef } from 'react';
2
2
  import MuiTextField, { OutlinedTextFieldProps } from '@mui/material/TextField';
3
- import { Button, InputAdornment } from '@mui/material';
3
+ import { Button, FormControl, FormHelperText, FormLabel, InputAdornment } from '@mui/material';
4
4
  import BrushIcon from '@mui/icons-material/Brush';
5
5
 
6
6
  export interface FieldTypeColorProps extends Omit<OutlinedTextFieldProps, 'variant'> {}
7
7
 
8
- const FieldTypeColor = ({InputProps, InputLabelProps, ...props }: FieldTypeColorProps) => {
8
+ const FieldTypeColor = ({InputProps, label, required, ...props }: FieldTypeColorProps) => {
9
9
 
10
10
  return (
11
+ <FormControl fullWidth required={required}>
12
+ <FormLabel>{label}</FormLabel>
11
13
  <MuiTextField
12
14
  size="small"
13
15
  variant='outlined'
@@ -31,14 +33,10 @@ const FieldTypeColor = ({InputProps, InputLabelProps, ...props }: FieldTypeColor
31
33
  // Spread props at the end to allow Input prop overrides
32
34
  ...InputProps,
33
35
  }}
34
- InputLabelProps={{
35
- shrink: true,
36
- // Spread props at the end to allow InputLabel prop overrides
37
- ...InputLabelProps,
38
- }}
39
36
  // Spread props at the end to allow prop overrides
40
37
  {...props}
41
38
  />
39
+ </FormControl>
42
40
  );
43
41
  };
44
42
 
@@ -19,8 +19,5 @@ const Template: Story<FieldTypeDateProps> = (args) => {
19
19
  export const Default = Template.bind({});
20
20
  Default.args = {
21
21
  label: 'Date label',
22
- textFieldProps: {
23
- helperText: 'Date helper text',
24
- error: false,
25
- }
22
+ helperText: 'Date helper text',
26
23
  };
@@ -1,36 +1,33 @@
1
1
  import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
2
2
  import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
3
3
  import { DatePicker, DatePickerProps} from '@mui/x-date-pickers/DatePicker';
4
- import { TextField, TextFieldProps } from '@mui/material';
4
+ import { FormControl, FormLabel, TextField } from '@mui/material';
5
5
 
6
6
  export interface FieldTypeDateProps extends Omit<DatePickerProps<Date, Date>, 'renderInput'> {
7
- /**
8
- * Props passed to TextField component
9
- */
10
- textFieldProps?: TextFieldProps;
7
+ helperText?: string;
8
+ error?: boolean;
9
+ required?: boolean;
11
10
  };
12
11
 
13
- const FieldTypeDate = ({textFieldProps, ...props}: FieldTypeDateProps) => {
12
+ const FieldTypeDate = ({label, helperText, error, required, ...props}: FieldTypeDateProps) => {
14
13
  return (
15
- <LocalizationProvider dateAdapter={AdapterDateFns}>
16
- <DatePicker
17
- data-testid="zds-date-picker"
18
- renderInput={(params) =>
19
- <TextField
20
- InputLabelProps={{
21
- shrink: true,
22
- // Spread props at the end to allow InputLabelProps overrides
23
- ...textFieldProps?.InputLabelProps,
24
- }}
25
- {...params}
26
- // Spread props at the end to allow textFieldProps overrides
27
- {...textFieldProps}
28
- />
29
- }
30
- // Spread props at the end to allow prop overrides
31
- {...props}
32
- />
33
- </LocalizationProvider>
14
+ <FormControl fullWidth required={required}>
15
+ <FormLabel>{label}</FormLabel>
16
+ <LocalizationProvider dateAdapter={AdapterDateFns}>
17
+ <DatePicker
18
+ data-testid="zds-date-picker"
19
+ renderInput={(params) =>
20
+ <TextField
21
+ {...params}
22
+ helperText={helperText}
23
+ error={error}
24
+ />
25
+ }
26
+ // Spread props at the end to allow prop overrides
27
+ {...props}
28
+ />
29
+ </LocalizationProvider>
30
+ </FormControl>
34
31
  );
35
32
  }
36
33
 
@@ -19,8 +19,5 @@ const Template: Story<FieldTypeDateTimeProps> = (args) => {
19
19
  export const Default = Template.bind({});
20
20
  Default.args = {
21
21
  label: 'Date label',
22
- textFieldProps: {
23
- helperText: 'Date helper text',
24
- error: false,
25
- }
22
+ helperText: 'Date helper text',
26
23
  };
@@ -1,36 +1,34 @@
1
1
  import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
2
2
  import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
3
3
  import { DateTimePicker, DateTimePickerProps} from '@mui/x-date-pickers/DateTimePicker';
4
- import { TextField, TextFieldProps } from '@mui/material';
4
+ import { TextField, FormControl, FormLabel } from '@mui/material';
5
5
 
6
6
  export interface FieldTypeDateTimeProps extends Omit<DateTimePickerProps<Date, Date>, 'renderInput'> {
7
- /**
8
- * Props passed to TextField component
9
- */
10
- textFieldProps?: TextFieldProps;
7
+ helperText?: string;
8
+ error?: boolean;
9
+ required?: boolean;
11
10
  };
12
11
 
13
- const FieldTypeDateTime = ({textFieldProps, ...props}: FieldTypeDateTimeProps) => {
12
+ const FieldTypeDateTime = ({label, helperText, error, required, ...props}: FieldTypeDateTimeProps) => {
14
13
  return (
15
- <LocalizationProvider dateAdapter={AdapterDateFns}>
16
- <DateTimePicker
17
- data-testid="zds-date-picker"
18
- renderInput={(params) =>
19
- <TextField
20
- InputLabelProps={{
21
- shrink: true,
22
- // Spread props at the end to allow InputLabelProps overrides
23
- ...textFieldProps?.InputLabelProps,
24
- }}
25
- {...params}
26
- // Spread props at the end to allow textFieldProps overrides
27
- {...textFieldProps}
28
- />
29
- }
30
- // Spread props at the end to allow prop overrides
31
- {...props}
32
- />
33
- </LocalizationProvider>
14
+ <FormControl fullWidth required={required}>
15
+ <FormLabel>{label}</FormLabel>
16
+ <LocalizationProvider dateAdapter={AdapterDateFns}>
17
+ <DateTimePicker
18
+ data-testid="zds-date-picker"
19
+ renderInput={(params) =>
20
+ <TextField
21
+
22
+ {...params}
23
+ helperText={helperText}
24
+ error={error}
25
+ />
26
+ }
27
+ // Spread props at the end to allow prop overrides
28
+ {...props}
29
+ />
30
+ </LocalizationProvider>
31
+ </FormControl>
34
32
  );
35
33
  }
36
34
 
@@ -44,7 +44,8 @@ const Template: Story<FieldTypeDropdownProps> = (args) => {
44
44
  export const Default = Template.bind({});
45
45
  Default.args = {
46
46
  placeholder: 'Placeholder Text...',
47
- label: 'Number label',
47
+ label: 'Dropdown label',
48
+ helperText: 'Dropdown helper text',
48
49
  };
49
50
 
50
51
 
@@ -1,5 +1,6 @@
1
1
  import { MenuItem } from '@mui/material';
2
2
  import MuiTextField, { OutlinedTextFieldProps } from '@mui/material/TextField';
3
+ import { FormControl, FormLabel } from '@mui/material';
3
4
 
4
5
  interface Option {
5
6
  label: string;
@@ -10,19 +11,15 @@ export interface FieldTypeDropdownProps extends Omit<OutlinedTextFieldProps, 'va
10
11
  options: Option[];
11
12
  }
12
13
 
13
- const FieldTypeDropdown = ({InputLabelProps, options, ...props }: FieldTypeDropdownProps) => {
14
+ const FieldTypeDropdown = ({label, options, required, ...props }: FieldTypeDropdownProps) => {
14
15
 
15
16
  return (
17
+ <FormControl fullWidth required={required}>
18
+ <FormLabel>{label}</FormLabel>
16
19
  <MuiTextField
17
20
  size="small"
18
21
  variant='outlined'
19
22
  select
20
- InputLabelProps={{
21
- shrink: true,
22
- // Spread props at the end to allow InputLabel prop overrides
23
- ...InputLabelProps,
24
- }}
25
- // Spread props at the end to allow prop overrides
26
23
  SelectProps={{
27
24
  displayEmpty: true,
28
25
  }}
@@ -37,6 +34,7 @@ const FieldTypeDropdown = ({InputLabelProps, options, ...props }: FieldTypeDropd
37
34
  </MenuItem>
38
35
  ))}
39
36
  </MuiTextField>
37
+ </FormControl>
40
38
  );
41
39
  };
42
40
 
@@ -28,6 +28,7 @@ export const Default = Template.bind({});
28
28
  Default.args = {
29
29
  placeholder: 'Placeholder Text...',
30
30
  label: 'Number label',
31
+ helperText: 'Number helper text',
31
32
  };
32
33
 
33
34
 
@@ -1,22 +1,21 @@
1
+ import { FormControl, FormLabel } from '@mui/material';
1
2
  import MuiTextField, { OutlinedTextFieldProps } from '@mui/material/TextField';
2
3
 
3
4
  export interface FieldTypeNumberProps extends Omit<OutlinedTextFieldProps, 'variant'> {}
4
5
 
5
- const FieldTypeText = ({InputLabelProps, ...props }: FieldTypeNumberProps) => {
6
+ const FieldTypeText = ({label, required, ...props }: FieldTypeNumberProps) => {
6
7
 
7
8
  return (
9
+ <FormControl fullWidth required={required}>
10
+ <FormLabel>{label}</FormLabel>
8
11
  <MuiTextField
9
12
  size="small"
10
13
  variant='outlined'
11
14
  type='number'
12
- InputLabelProps={{
13
- shrink: true,
14
- // Spread props at the end to allow InputLabel prop overrides
15
- ...InputLabelProps,
16
- }}
17
15
  // Spread props at the end to allow prop overrides
18
16
  {...props}
19
17
  />
18
+ </FormControl>
20
19
  );
21
20
  };
22
21
 
@@ -0,0 +1,47 @@
1
+ import { ReactNode, SyntheticEvent, useState } from 'react';
2
+ import { Story, Meta } from '@storybook/react/types-6-0';
3
+ import FieldTypeOneToMany, { FieldTypeOneToManyProps } from '.';
4
+
5
+ export default {
6
+ title: 'FieldTypeOneToMany',
7
+ component: FieldTypeOneToMany,
8
+ argType: {},
9
+ } as Meta;
10
+
11
+ const Template: Story<FieldTypeOneToManyProps> = (args) => {
12
+ const [value, setValue] = useState<{component: string | ReactNode, value: string, inputLabel: string}[]>([]);
13
+
14
+ const [options, setOptions] = useState<{component: string | ReactNode, value: string, inputLabel: string}[]>([]);
15
+
16
+
17
+ const handleOnOpen = async () => {
18
+ const largeArr = new Array(1000).fill(null);
19
+ await new Promise((resolve) => setTimeout(resolve, 3000))
20
+ const data = largeArr.map((_, idx) => ({component: <div>{`Test ${idx}`}</div>, value: String(Math.random()), inputLabel: `Test ${idx}`}));
21
+ setOptions(data);
22
+ }
23
+
24
+ const handleOnChange = (e: SyntheticEvent<Element, Event>, values: {component: string | ReactNode, value: string, inputLabel: string}[]) => {
25
+ setValue(values);
26
+ }
27
+
28
+ return (
29
+ <FieldTypeOneToMany
30
+ {...args}
31
+ value={value}
32
+ onChange={handleOnChange}
33
+ options={options}
34
+ onOpen={handleOnOpen}
35
+ />
36
+ );
37
+ };
38
+
39
+ export const Default = Template.bind({});
40
+ Default.args = {
41
+ placeholder: 'Placeholder Text...',
42
+ label: 'OneToMany label',
43
+ helperText: 'OneToMany helperText',
44
+ };
45
+
46
+
47
+
@@ -0,0 +1,90 @@
1
+ import { ReactNode, useState } from 'react';
2
+ import { AutocompleteProps, FormControl, FormLabel, Popper, styled, TextField, TextFieldProps } from '@mui/material';
3
+ import Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete';
4
+ import { ListboxComponent } from '../utils/virtualization';
5
+
6
+ export interface FieldTypeOneToManyProps extends Omit<AutocompleteProps<any, false, false, false>, 'onOpen' | 'renderInput'> {
7
+ label?: string;
8
+ helperText?: string;
9
+ placeholder?: string;
10
+ error?: boolean;
11
+ required?: boolean;
12
+ /**
13
+ * Callback to be fired upon opening the dropdown
14
+ */
15
+ onOpen: () => Promise<any>;
16
+ /**
17
+ * Structure for option
18
+ */
19
+ options: {
20
+ /**
21
+ * Component to be rendered in the dropdown
22
+ */
23
+ component: ReactNode | string;
24
+ /**
25
+ * Value of option
26
+ */
27
+ value: string;
28
+ /**
29
+ * Label that should display in the input when selected
30
+ */
31
+ inputLabel: string;
32
+ }[]
33
+ }
34
+
35
+ const FieldTypeOneToMany = ({label, helperText, placeholder, error, onOpen, options, required, ...props }: FieldTypeOneToManyProps) => {
36
+ const [loaded, setLoaded] = useState(false);
37
+ const [loading, setLoading] = useState(false);
38
+
39
+ const handleOpen = () => {
40
+ if (!loaded && onOpen) {
41
+ onOpen().then(() => {
42
+ setLoading(false);
43
+ });
44
+ setLoading(true);
45
+ setLoaded(true);
46
+ }
47
+ };
48
+
49
+ return (
50
+ <FormControl fullWidth required={required}>
51
+ <FormLabel>{label}</FormLabel>
52
+ <Autocomplete
53
+ onOpen={handleOpen}
54
+ loading={loading}
55
+ fullWidth
56
+ multiple
57
+ disableListWrap
58
+ disableClearable
59
+ disablePortal
60
+ size='small'
61
+ PopperComponent={StyledPopper}
62
+ ListboxComponent={ListboxComponent}
63
+ renderInput={(params) => (
64
+ <TextField
65
+ {...params}
66
+ helperText={helperText}
67
+ error={error}
68
+ placeholder={placeholder}
69
+ />
70
+ )}
71
+ options={loading ? [] : options}
72
+ getOptionLabel={(option) => option.inputLabel}
73
+ renderOption={(props, option) => [props, option.component]}
74
+ {...props}
75
+ />
76
+ </FormControl>
77
+ );
78
+ };
79
+
80
+ export default FieldTypeOneToMany;
81
+
82
+ const StyledPopper = styled(Popper)({
83
+ [`& .${autocompleteClasses.listbox}`]: {
84
+ boxSizing: 'border-box',
85
+ '& ul': {
86
+ padding: 0,
87
+ margin: 0,
88
+ },
89
+ },
90
+ });