@qoretechnologies/reqraft 0.2.2 → 0.2.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.
- package/package.json +1 -1
- package/src/components/form/fields/Field.tsx +0 -37
- package/src/components/form/fields/string/String.stories.tsx +0 -23
- package/src/components/form/fields/string/String.tsx +0 -68
- package/src/components/menu/Menu.stories.tsx +0 -73
- package/src/components/menu/Menu.tsx +0 -244
- package/src/contexts/FetchContext.tsx +0 -25
- package/src/contexts/ReqraftContext.tsx +0 -16
- package/src/contexts/StorageContext.tsx +0 -33
- package/src/hooks/useFetch/useFetch.stories.tsx +0 -123
- package/src/hooks/useFetch/useFetch.tsx +0 -71
- package/src/hooks/useReqraftProperty.ts +0 -16
- package/src/hooks/useStorage/useStorage.stories.tsx +0 -84
- package/src/hooks/useStorage/useStorage.ts +0 -30
- package/src/hooks/useValidation.ts +0 -9
- package/src/index.tsx +0 -12
- package/src/providers/FetchProvider.tsx +0 -62
- package/src/providers/ReqraftProvider.tsx +0 -33
- package/src/providers/StorageProvider.tsx +0 -80
- package/src/types/Form.ts +0 -57
- package/src/types.ts +0 -12
- package/src/utils/fetch.ts +0 -121
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qoretechnologies/reqraft",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "ReQraft is a collection of React components and hooks that are used across Qore Technologies' products made using the ReQore component library from Qore.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { TFormFieldType, TFormFieldValueType } from '../../../types/Form';
|
|
2
|
-
import { FormStringField } from './string/String';
|
|
3
|
-
|
|
4
|
-
export interface IFormFieldProps<T extends TFormFieldType = TFormFieldType> {
|
|
5
|
-
type?: T;
|
|
6
|
-
value: TFormFieldValueType<T>;
|
|
7
|
-
onChange: (value: TFormFieldValueType<T>, event?: unknown) => void;
|
|
8
|
-
|
|
9
|
-
validateSelf?: boolean;
|
|
10
|
-
onValidateChange?: (isValid: boolean) => void;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export const FormField = <T extends TFormFieldType>({
|
|
14
|
-
type,
|
|
15
|
-
onChange,
|
|
16
|
-
...rest
|
|
17
|
-
}: IFormFieldProps<T>) => {
|
|
18
|
-
const handleChange = (value: TFormFieldValueType<T>, event?: unknown) => {
|
|
19
|
-
onChange(value, event);
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const renderField = (type: T) => {
|
|
23
|
-
switch (type) {
|
|
24
|
-
case 'string':
|
|
25
|
-
return (
|
|
26
|
-
<FormStringField
|
|
27
|
-
{...rest}
|
|
28
|
-
onChange={(value: string) => handleChange(value as TFormFieldValueType<T>)}
|
|
29
|
-
/>
|
|
30
|
-
);
|
|
31
|
-
default:
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
return renderField(type);
|
|
37
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { StoryObj } from '@storybook/react';
|
|
2
|
-
import { StoryMeta } from '../../../../types';
|
|
3
|
-
import { FormStringField } from './String';
|
|
4
|
-
|
|
5
|
-
const meta = {
|
|
6
|
-
component: FormStringField,
|
|
7
|
-
title: 'Components/Form/String',
|
|
8
|
-
} as StoryMeta<typeof FormStringField>;
|
|
9
|
-
|
|
10
|
-
export default meta;
|
|
11
|
-
type Story = StoryObj<typeof meta>;
|
|
12
|
-
|
|
13
|
-
export const Default: Story = {};
|
|
14
|
-
export const Value: Story = {
|
|
15
|
-
args: {
|
|
16
|
-
value: 'Hello, World!',
|
|
17
|
-
},
|
|
18
|
-
};
|
|
19
|
-
export const WithLabel: Story = {
|
|
20
|
-
args: {
|
|
21
|
-
label: 'Label',
|
|
22
|
-
},
|
|
23
|
-
};
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { ReqoreControlGroup, ReqoreInput, ReqoreTag } from '@qoretechnologies/reqore';
|
|
2
|
-
import { IReqoreControlGroupProps } from '@qoretechnologies/reqore/dist/components/ControlGroup';
|
|
3
|
-
import { IReqoreInputProps } from '@qoretechnologies/reqore/dist/components/Input';
|
|
4
|
-
import { IReqoreTagProps } from '@qoretechnologies/reqore/dist/components/Tag';
|
|
5
|
-
import { ChangeEvent, useCallback } from 'react';
|
|
6
|
-
import { TFormFieldValueType } from '../../../../types/Form';
|
|
7
|
-
|
|
8
|
-
export interface IStringFormFieldProps extends Omit<IReqoreInputProps, 'onChange' | 'value'> {
|
|
9
|
-
sensitive?: boolean;
|
|
10
|
-
value?: TFormFieldValueType<'string'>;
|
|
11
|
-
label?: IReqoreTagProps['label'];
|
|
12
|
-
labelPosition?: 'top' | 'left' | 'right' | 'bottom';
|
|
13
|
-
labelProps?: IReqoreTagProps;
|
|
14
|
-
wrapperProps?: IReqoreControlGroupProps;
|
|
15
|
-
onChange?: (value?: TFormFieldValueType<'string'>, event?: ChangeEvent<HTMLInputElement>) => void;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const FormStringField = ({
|
|
19
|
-
onChange,
|
|
20
|
-
wrapperProps,
|
|
21
|
-
labelProps,
|
|
22
|
-
label,
|
|
23
|
-
labelPosition = 'top',
|
|
24
|
-
sensitive,
|
|
25
|
-
...rest
|
|
26
|
-
}: IStringFormFieldProps) => {
|
|
27
|
-
// When input value changes
|
|
28
|
-
const handleChange = useCallback((event: ChangeEvent<HTMLInputElement>): void => {
|
|
29
|
-
event.persist();
|
|
30
|
-
|
|
31
|
-
onChange(event.target.value?.toString(), event);
|
|
32
|
-
}, []);
|
|
33
|
-
|
|
34
|
-
// Clear the input on reset click
|
|
35
|
-
const handleClearClick = useCallback((): void => {
|
|
36
|
-
onChange('');
|
|
37
|
-
}, []);
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<ReqoreControlGroup
|
|
41
|
-
stack
|
|
42
|
-
{...wrapperProps}
|
|
43
|
-
vertical={labelPosition === 'bottom' || labelPosition === 'top'}
|
|
44
|
-
>
|
|
45
|
-
{(label || label === 0) && (labelPosition === 'top' || labelPosition === 'left') ? (
|
|
46
|
-
<ReqoreTag label={label} fluid {...labelProps} />
|
|
47
|
-
) : null}
|
|
48
|
-
<ReqoreInput
|
|
49
|
-
fluid
|
|
50
|
-
onFocus={(event) => {
|
|
51
|
-
event.stopPropagation();
|
|
52
|
-
rest?.onFocus?.(event);
|
|
53
|
-
}}
|
|
54
|
-
onClick={(event) => {
|
|
55
|
-
event.stopPropagation();
|
|
56
|
-
rest?.onClick?.(event);
|
|
57
|
-
}}
|
|
58
|
-
onChange={handleChange}
|
|
59
|
-
type={sensitive ? 'password' : 'text'}
|
|
60
|
-
onClearClick={handleClearClick}
|
|
61
|
-
{...rest}
|
|
62
|
-
/>
|
|
63
|
-
{(label || label === 0) && (labelPosition === 'bottom' || labelPosition === 'right') ? (
|
|
64
|
-
<ReqoreTag label={label} fluid {...labelProps} />
|
|
65
|
-
) : null}
|
|
66
|
-
</ReqoreControlGroup>
|
|
67
|
-
);
|
|
68
|
-
};
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { StoryObj } from '@storybook/react';
|
|
2
|
-
import { expect, fireEvent, fn, waitFor } from '@storybook/test';
|
|
3
|
-
import { storiesStorageMock, storiesStorageMockEmpty } from '../../../__tests__/ mock';
|
|
4
|
-
import { testsWaitForText } from '../../../__tests__/utils';
|
|
5
|
-
import menu from '../../../mock/menu';
|
|
6
|
-
import { StoryMeta } from '../../types';
|
|
7
|
-
import { ReqraftMenu, TReqraftMenu } from './Menu';
|
|
8
|
-
|
|
9
|
-
const typedMenu = menu as TReqraftMenu;
|
|
10
|
-
|
|
11
|
-
const meta = {
|
|
12
|
-
component: ReqraftMenu,
|
|
13
|
-
title: 'Components/Menu',
|
|
14
|
-
render: (props) => <ReqraftMenu {...props} />,
|
|
15
|
-
parameters: {
|
|
16
|
-
mockData: [...storiesStorageMockEmpty],
|
|
17
|
-
},
|
|
18
|
-
} as StoryMeta<typeof ReqraftMenu>;
|
|
19
|
-
|
|
20
|
-
export default meta;
|
|
21
|
-
export type Story = StoryObj<typeof meta>;
|
|
22
|
-
|
|
23
|
-
export const Basic: Story = {
|
|
24
|
-
args: {
|
|
25
|
-
menu: typedMenu,
|
|
26
|
-
},
|
|
27
|
-
play: async () => {
|
|
28
|
-
await testsWaitForText('Developer Portal');
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
export const ActivePath: Story = {
|
|
32
|
-
args: {
|
|
33
|
-
path: '/Interfaces/mapper',
|
|
34
|
-
menu: typedMenu,
|
|
35
|
-
},
|
|
36
|
-
play: async () => {
|
|
37
|
-
await testsWaitForText('Developer Portal');
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export const WithDefaultQuery: Story = {
|
|
42
|
-
args: {
|
|
43
|
-
menu: typedMenu,
|
|
44
|
-
defaultQuery: 'mapper',
|
|
45
|
-
},
|
|
46
|
-
play: async () => {
|
|
47
|
-
await testsWaitForText('Developer Portal');
|
|
48
|
-
await expect(document.querySelector('.reqore-input')).toHaveValue('mapper');
|
|
49
|
-
await expect(document.querySelectorAll('.reqore-menu-item')).toHaveLength(2);
|
|
50
|
-
},
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export const Filtered: Story = {
|
|
54
|
-
args: {
|
|
55
|
-
menu: typedMenu,
|
|
56
|
-
onQueryChange: fn(),
|
|
57
|
-
},
|
|
58
|
-
play: async () => {
|
|
59
|
-
await testsWaitForText('Developer Portal');
|
|
60
|
-
await fireEvent.change(document.querySelector('.reqore-input'), { target: { value: 'step' } });
|
|
61
|
-
|
|
62
|
-
await waitFor(() => expect(document.querySelectorAll('.reqore-menu-item')).toHaveLength(2), {
|
|
63
|
-
timeout: 1000,
|
|
64
|
-
});
|
|
65
|
-
},
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
export const WidthFromStorage: Story = {
|
|
69
|
-
...ActivePath,
|
|
70
|
-
parameters: {
|
|
71
|
-
mockData: [...storiesStorageMock],
|
|
72
|
-
},
|
|
73
|
-
};
|
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ReqoreButton,
|
|
3
|
-
ReqoreControlGroup,
|
|
4
|
-
ReqoreInput,
|
|
5
|
-
ReqoreMenuDivider,
|
|
6
|
-
ReqoreMenuItem,
|
|
7
|
-
ReqoreMenuSection,
|
|
8
|
-
} from '@qoretechnologies/reqore';
|
|
9
|
-
import ReqoreMenu, { IReqoreMenuProps } from '@qoretechnologies/reqore/dist/components/Menu';
|
|
10
|
-
import { IReqoreMenuDividerProps } from '@qoretechnologies/reqore/dist/components/Menu/divider';
|
|
11
|
-
import { IReqoreMenuItemProps } from '@qoretechnologies/reqore/dist/components/Menu/item';
|
|
12
|
-
import { map, reduce, size } from 'lodash';
|
|
13
|
-
import { useEffect, useMemo, useState } from 'react';
|
|
14
|
-
import { useReqraftStorage } from '../../hooks/useStorage/useStorage';
|
|
15
|
-
|
|
16
|
-
export interface IReqraftMenuItem extends IReqoreMenuItemProps {
|
|
17
|
-
submenu?: TReqraftMenuItem[];
|
|
18
|
-
activePaths?: string[];
|
|
19
|
-
to?: string;
|
|
20
|
-
href?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type TReqraftMenuItem = IReqraftMenuItem | ({ divider: true } & IReqoreMenuDividerProps);
|
|
24
|
-
export type TReqraftMenu = TReqraftMenuItem[];
|
|
25
|
-
|
|
26
|
-
export interface IReqraftMenuProps extends Partial<Omit<IReqoreMenuProps, 'resizable'>> {
|
|
27
|
-
hidden?: boolean;
|
|
28
|
-
onHideClick?: () => void;
|
|
29
|
-
|
|
30
|
-
defaultQuery?: string;
|
|
31
|
-
onQueryChange?: (query: string) => void;
|
|
32
|
-
|
|
33
|
-
menu: TReqraftMenu;
|
|
34
|
-
inputFocusShortcut?: string;
|
|
35
|
-
path?: string;
|
|
36
|
-
|
|
37
|
-
resizable?: boolean;
|
|
38
|
-
onResizeChange?: (width: number) => void;
|
|
39
|
-
defaultWidth?: number;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export const ReqraftMenuItem = ({
|
|
43
|
-
path,
|
|
44
|
-
isCollapsed,
|
|
45
|
-
...props
|
|
46
|
-
}: TReqraftMenuItem & { path?: string; isCollapsed?: boolean }) => {
|
|
47
|
-
if ('divider' in props) {
|
|
48
|
-
return <ReqoreMenuDivider />;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const isActive = useMemo(
|
|
52
|
-
() =>
|
|
53
|
-
props.activePaths?.some(
|
|
54
|
-
(activePath) => activePath === path || path?.startsWith(`${activePath}/`)
|
|
55
|
-
),
|
|
56
|
-
[path]
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
if (props.submenu) {
|
|
60
|
-
const { submenu, ...menuData } = props;
|
|
61
|
-
|
|
62
|
-
return (
|
|
63
|
-
<ReqoreMenuSection
|
|
64
|
-
label={menuData.label}
|
|
65
|
-
icon={menuData.icon}
|
|
66
|
-
isCollapsed={isCollapsed && !isActive}
|
|
67
|
-
verticalPadding='tiny'
|
|
68
|
-
{...menuData}
|
|
69
|
-
>
|
|
70
|
-
{map(submenu, (submenuData, submenuId) => (
|
|
71
|
-
<ReqraftMenuItem key={submenuId} {...submenuData} path={path} />
|
|
72
|
-
))}
|
|
73
|
-
</ReqoreMenuSection>
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return (
|
|
78
|
-
<ReqoreMenuItem
|
|
79
|
-
customTheme={{ main: '#050505' }}
|
|
80
|
-
effect={
|
|
81
|
-
isActive
|
|
82
|
-
? {
|
|
83
|
-
gradient: {
|
|
84
|
-
colors: {
|
|
85
|
-
0: 'info:darken:5:0.4',
|
|
86
|
-
40: '#181818',
|
|
87
|
-
100: '#181818',
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
}
|
|
91
|
-
: undefined
|
|
92
|
-
}
|
|
93
|
-
leftIconColor={isActive ? 'info:lighten:10' : undefined}
|
|
94
|
-
verticalPadding='tiny'
|
|
95
|
-
{...props}
|
|
96
|
-
/>
|
|
97
|
-
);
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
export const ReqraftMenu = ({
|
|
101
|
-
defaultQuery,
|
|
102
|
-
defaultWidth = 250,
|
|
103
|
-
inputFocusShortcut = '/',
|
|
104
|
-
hidden,
|
|
105
|
-
menu,
|
|
106
|
-
onQueryChange,
|
|
107
|
-
onResizeChange,
|
|
108
|
-
onHideClick,
|
|
109
|
-
resizable,
|
|
110
|
-
path,
|
|
111
|
-
...rest
|
|
112
|
-
}: IReqraftMenuProps) => {
|
|
113
|
-
const [query, setQuery] = useState<string>(defaultQuery);
|
|
114
|
-
|
|
115
|
-
const [isSidebarOpen, update] = useReqraftStorage<boolean>('sidebar-open', true, false);
|
|
116
|
-
const [sidebarSize, updateSidebarSize] = useReqraftStorage<number>(
|
|
117
|
-
'sidebar-size',
|
|
118
|
-
defaultWidth,
|
|
119
|
-
false
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
useEffect(() => {
|
|
123
|
-
if (defaultQuery) {
|
|
124
|
-
setQuery(defaultQuery);
|
|
125
|
-
}
|
|
126
|
-
}, [defaultQuery]);
|
|
127
|
-
|
|
128
|
-
const handleQueryChange = (newQuery: string) => {
|
|
129
|
-
setQuery(newQuery);
|
|
130
|
-
onQueryChange?.(newQuery);
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const handleWidthChange = (newWidth: number) => {
|
|
134
|
-
updateSidebarSize(newWidth);
|
|
135
|
-
onResizeChange?.(newWidth);
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const filteredMenu: TReqraftMenu = useMemo<TReqraftMenu>(() => {
|
|
139
|
-
if (!query) {
|
|
140
|
-
return menu;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const filterItems = (items: TReqraftMenu): TReqraftMenu => {
|
|
144
|
-
return reduce(
|
|
145
|
-
items,
|
|
146
|
-
(acc, item) => {
|
|
147
|
-
if ('divider' in item) {
|
|
148
|
-
acc.push(item);
|
|
149
|
-
return acc;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (item.submenu) {
|
|
153
|
-
const submenu = filterItems(item.submenu);
|
|
154
|
-
const hasChildMatch = size(submenu);
|
|
155
|
-
|
|
156
|
-
if (hasChildMatch) {
|
|
157
|
-
acc.push({
|
|
158
|
-
...item,
|
|
159
|
-
submenu,
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
return acc;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (item.label.toString().toLowerCase().includes(query.toLowerCase())) {
|
|
167
|
-
acc.push(item);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return acc;
|
|
171
|
-
},
|
|
172
|
-
[]
|
|
173
|
-
);
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
return filterItems(menu);
|
|
177
|
-
}, [menu, query]);
|
|
178
|
-
|
|
179
|
-
if (hidden) {
|
|
180
|
-
return null;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return (
|
|
184
|
-
<ReqoreMenu
|
|
185
|
-
width='250px'
|
|
186
|
-
minimal
|
|
187
|
-
position='left'
|
|
188
|
-
resizable={{
|
|
189
|
-
enable: { right: resizable, left: false },
|
|
190
|
-
minWidth: '250px',
|
|
191
|
-
maxWidth: '350px',
|
|
192
|
-
onResizeStop: (_e, _direction, _ref, d) => {
|
|
193
|
-
handleWidthChange(sidebarSize + d.width);
|
|
194
|
-
},
|
|
195
|
-
size: {
|
|
196
|
-
width: `${sidebarSize}px`,
|
|
197
|
-
height: '100%',
|
|
198
|
-
},
|
|
199
|
-
}}
|
|
200
|
-
rounded={false}
|
|
201
|
-
customTheme={{ main: '#181818' }}
|
|
202
|
-
{...rest}
|
|
203
|
-
>
|
|
204
|
-
<ReqoreControlGroup>
|
|
205
|
-
<ReqoreInput
|
|
206
|
-
icon='Search2Line'
|
|
207
|
-
minimal={false}
|
|
208
|
-
flat={false}
|
|
209
|
-
placeholder={`Filter menu "${inputFocusShortcut}"`}
|
|
210
|
-
intent={query ? 'info' : 'muted'}
|
|
211
|
-
leftIconProps={{ size: 'small' }}
|
|
212
|
-
iconColor={query ? 'info' : 'muted'}
|
|
213
|
-
pill
|
|
214
|
-
value={query}
|
|
215
|
-
onClearClick={() => handleQueryChange('')}
|
|
216
|
-
onChange={(e: any) => handleQueryChange(e.target.value)}
|
|
217
|
-
focusRules={{
|
|
218
|
-
shortcut: inputFocusShortcut,
|
|
219
|
-
type: 'keypress',
|
|
220
|
-
clearOnFocus: true,
|
|
221
|
-
doNotInsertShortcut: true,
|
|
222
|
-
}}
|
|
223
|
-
/>
|
|
224
|
-
<ReqoreButton
|
|
225
|
-
icon='SideBarLine'
|
|
226
|
-
fixed
|
|
227
|
-
minimal={false}
|
|
228
|
-
onClick={() => {
|
|
229
|
-
update(!isSidebarOpen);
|
|
230
|
-
onHideClick?.();
|
|
231
|
-
}}
|
|
232
|
-
/>
|
|
233
|
-
</ReqoreControlGroup>
|
|
234
|
-
{map(filteredMenu, (menuData, menuId) => (
|
|
235
|
-
<ReqraftMenuItem
|
|
236
|
-
key={menuId}
|
|
237
|
-
{...menuData}
|
|
238
|
-
path={path}
|
|
239
|
-
isCollapsed={!query && !!(menuData as IReqraftMenuItem).submenu}
|
|
240
|
-
/>
|
|
241
|
-
))}
|
|
242
|
-
</ReqoreMenu>
|
|
243
|
-
);
|
|
244
|
-
};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { createContext } from 'use-context-selector';
|
|
2
|
-
import { IReqraftFetchResponse, IReqraftQueryConfig } from '../utils/fetch';
|
|
3
|
-
|
|
4
|
-
export type TReqraftContextQueryConfig = Omit<IReqraftQueryConfig, 'method'>;
|
|
5
|
-
export interface IReqraftFetchContext {
|
|
6
|
-
get: <T>(config?: TReqraftContextQueryConfig) => Promise<IReqraftFetchResponse<T>>;
|
|
7
|
-
post: <T>(config?: TReqraftContextQueryConfig) => Promise<IReqraftFetchResponse<T>>;
|
|
8
|
-
put: <T>(config?: TReqraftContextQueryConfig) => Promise<IReqraftFetchResponse<T>>;
|
|
9
|
-
del: <T>(config?: TReqraftContextQueryConfig) => Promise<IReqraftFetchResponse<T>>;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const FetchContext = createContext<IReqraftFetchContext>({
|
|
13
|
-
get: async () => {
|
|
14
|
-
throw new Error('FetchContext not implemented');
|
|
15
|
-
},
|
|
16
|
-
post: async () => {
|
|
17
|
-
throw new Error('FetchContext not implemented');
|
|
18
|
-
},
|
|
19
|
-
put: async () => {
|
|
20
|
-
throw new Error('FetchContext not implemented');
|
|
21
|
-
},
|
|
22
|
-
del: async () => {
|
|
23
|
-
throw new Error('FetchContext not implemented');
|
|
24
|
-
},
|
|
25
|
-
});
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { createContext } from 'use-context-selector';
|
|
2
|
-
import { IReqraftFetchConfig } from '../utils/fetch';
|
|
3
|
-
|
|
4
|
-
export interface IReqraftContext {
|
|
5
|
-
appName: string;
|
|
6
|
-
instance?: string;
|
|
7
|
-
instanceToken: string;
|
|
8
|
-
instanceUnauthorizedRedirect?: IReqraftFetchConfig['unauthorizedRedirect'];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const ReqraftContext = createContext<IReqraftContext>({
|
|
12
|
-
appName: '',
|
|
13
|
-
instance: '',
|
|
14
|
-
instanceToken: '',
|
|
15
|
-
instanceUnauthorizedRedirect: undefined,
|
|
16
|
-
});
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { Get } from 'type-fest';
|
|
2
|
-
import { createContext } from 'use-context-selector';
|
|
3
|
-
import { TReqraftStorageValue } from '../hooks/useStorage/useStorage';
|
|
4
|
-
|
|
5
|
-
export type TReqraftStorage = Record<string, any>;
|
|
6
|
-
|
|
7
|
-
export interface IReqraftStorageContext {
|
|
8
|
-
storage?: Record<string, any>;
|
|
9
|
-
getStorage?: <T extends TReqraftStorageValue>(
|
|
10
|
-
path: string,
|
|
11
|
-
defaultValue?: T,
|
|
12
|
-
includeAppPrefix?: boolean
|
|
13
|
-
) => Get<TReqraftStorage, string>;
|
|
14
|
-
updateStorage?: <T extends TReqraftStorageValue>(
|
|
15
|
-
path: string,
|
|
16
|
-
value: T,
|
|
17
|
-
includeAppPrefix?: boolean
|
|
18
|
-
) => void;
|
|
19
|
-
removeStorageValue: (path: string, includeAppPrefix?: boolean) => void;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const ReqraftStorageContext = createContext<IReqraftStorageContext>({
|
|
23
|
-
storage: {},
|
|
24
|
-
getStorage: () => {
|
|
25
|
-
throw new Error('Storage not implemented');
|
|
26
|
-
},
|
|
27
|
-
updateStorage: () => {
|
|
28
|
-
throw new Error('Storage not implemented');
|
|
29
|
-
},
|
|
30
|
-
removeStorageValue: () => {
|
|
31
|
-
throw new Error('Storage not implemented');
|
|
32
|
-
},
|
|
33
|
-
});
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { ReqoreSpinner, ReqoreTree } from '@qoretechnologies/reqore';
|
|
2
|
-
import { StoryObj } from '@storybook/react';
|
|
3
|
-
import { useEffectOnce } from 'react-use';
|
|
4
|
-
import { testsWaitForText } from '../../../__tests__/utils';
|
|
5
|
-
import { StoryMeta } from '../../types';
|
|
6
|
-
import { useFetch } from './useFetch';
|
|
7
|
-
|
|
8
|
-
const meta = {
|
|
9
|
-
title: 'Hooks/useFetch',
|
|
10
|
-
render: (args) => {
|
|
11
|
-
const { data = {}, load, loading } = useFetch<any>({ url: 'public/info', method: args.method });
|
|
12
|
-
|
|
13
|
-
useEffectOnce(() => {
|
|
14
|
-
load();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
return loading ? (
|
|
18
|
-
<ReqoreSpinner />
|
|
19
|
-
) : (
|
|
20
|
-
<ReqoreTree data={data} bottomActions={[{ label: 'Refetch', onClick: load }]} />
|
|
21
|
-
);
|
|
22
|
-
},
|
|
23
|
-
} as StoryMeta<any>;
|
|
24
|
-
|
|
25
|
-
export default meta;
|
|
26
|
-
export type Story = StoryObj<typeof meta>;
|
|
27
|
-
|
|
28
|
-
export const get: Story = {
|
|
29
|
-
args: {
|
|
30
|
-
method: 'GET',
|
|
31
|
-
},
|
|
32
|
-
parameters: {
|
|
33
|
-
mockData: [
|
|
34
|
-
{
|
|
35
|
-
// An array of mock objects which will add in every story
|
|
36
|
-
url: 'https://hq.qoretechnologies.com:8092/api/latest/public/info',
|
|
37
|
-
method: 'GET',
|
|
38
|
-
status: 200,
|
|
39
|
-
delay: 200,
|
|
40
|
-
response: {
|
|
41
|
-
'instance-key': 'rippy-dev1',
|
|
42
|
-
'omq-version': '6.1.0_prod',
|
|
43
|
-
'omq-build': 'd115c45fbed95c03327aea880be455137e9778ba',
|
|
44
|
-
'qore-version': '2.0.0',
|
|
45
|
-
'omq-schema': 'omq@omq',
|
|
46
|
-
edition: 'Enterprise',
|
|
47
|
-
tz_region: 'Europe/Prague',
|
|
48
|
-
tz_utc_offset: 3600,
|
|
49
|
-
noauth: false,
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
],
|
|
53
|
-
},
|
|
54
|
-
play: async () => {
|
|
55
|
-
await testsWaitForText('"rippy-dev1"');
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export const put: Story = {
|
|
60
|
-
args: {
|
|
61
|
-
method: 'PUT',
|
|
62
|
-
},
|
|
63
|
-
parameters: {
|
|
64
|
-
mockData: [
|
|
65
|
-
{
|
|
66
|
-
// An array of mock objects which will add in every story
|
|
67
|
-
url: 'https://hq.qoretechnologies.com:8092/api/latest/public/info',
|
|
68
|
-
method: 'PUT',
|
|
69
|
-
status: 200,
|
|
70
|
-
response: {
|
|
71
|
-
status: 'Successfuly updated',
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
},
|
|
76
|
-
play: async () => {
|
|
77
|
-
await testsWaitForText('"Successfuly updated"');
|
|
78
|
-
},
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
export const post: Story = {
|
|
82
|
-
args: {
|
|
83
|
-
method: 'POST',
|
|
84
|
-
},
|
|
85
|
-
parameters: {
|
|
86
|
-
mockData: [
|
|
87
|
-
{
|
|
88
|
-
// An array of mock objects which will add in every story
|
|
89
|
-
url: 'https://hq.qoretechnologies.com:8092/api/latest/public/info',
|
|
90
|
-
method: 'POST',
|
|
91
|
-
status: 200,
|
|
92
|
-
response: {
|
|
93
|
-
status: 'Successfuly created',
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
],
|
|
97
|
-
},
|
|
98
|
-
play: async () => {
|
|
99
|
-
await testsWaitForText('"Successfuly created"');
|
|
100
|
-
},
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
export const del: Story = {
|
|
104
|
-
args: {
|
|
105
|
-
method: 'DELETE',
|
|
106
|
-
},
|
|
107
|
-
parameters: {
|
|
108
|
-
mockData: [
|
|
109
|
-
{
|
|
110
|
-
// An array of mock objects which will add in every story
|
|
111
|
-
url: 'https://hq.qoretechnologies.com:8092/api/latest/public/info',
|
|
112
|
-
method: 'DELETE',
|
|
113
|
-
status: 200,
|
|
114
|
-
response: {
|
|
115
|
-
status: 'Successfuly deleted',
|
|
116
|
-
},
|
|
117
|
-
},
|
|
118
|
-
],
|
|
119
|
-
},
|
|
120
|
-
play: async () => {
|
|
121
|
-
await testsWaitForText('"Successfuly deleted"');
|
|
122
|
-
},
|
|
123
|
-
};
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
import { useEffectOnce } from 'react-use';
|
|
3
|
-
import { useContextSelector } from 'use-context-selector';
|
|
4
|
-
import { FetchContext } from '../../contexts/FetchContext';
|
|
5
|
-
import { IReqraftQueryConfig } from '../../utils/fetch';
|
|
6
|
-
|
|
7
|
-
export interface IReqraftUseFetch<T> {
|
|
8
|
-
data: T | undefined;
|
|
9
|
-
loading: boolean;
|
|
10
|
-
load: () => Promise<T>;
|
|
11
|
-
error: Error | undefined;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface IReqraftUseFetchOptions<T> extends IReqraftQueryConfig {
|
|
15
|
-
defaultData?: T;
|
|
16
|
-
loadOnMount?: boolean;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function useFetch<T>({
|
|
20
|
-
url,
|
|
21
|
-
method = 'GET',
|
|
22
|
-
body,
|
|
23
|
-
cache,
|
|
24
|
-
defaultData,
|
|
25
|
-
loadOnMount,
|
|
26
|
-
}: IReqraftUseFetchOptions<T>) {
|
|
27
|
-
const query = useContextSelector(FetchContext, (context) => {
|
|
28
|
-
switch (method) {
|
|
29
|
-
case 'GET':
|
|
30
|
-
return context.get;
|
|
31
|
-
case 'POST':
|
|
32
|
-
return context.post;
|
|
33
|
-
case 'PUT':
|
|
34
|
-
return context.put;
|
|
35
|
-
case 'DELETE':
|
|
36
|
-
return context.del;
|
|
37
|
-
default:
|
|
38
|
-
throw new Error('Invalid method');
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const [loading, setLoading] = useState(loadOnMount);
|
|
43
|
-
const [data, setData] = useState<T | undefined>(defaultData);
|
|
44
|
-
const [error, setError] = useState<Error | undefined>();
|
|
45
|
-
|
|
46
|
-
async function load({
|
|
47
|
-
body: customBody,
|
|
48
|
-
mergeBodies,
|
|
49
|
-
}: { body?: Record<string | number, any>; mergeBodies?: boolean } = {}) {
|
|
50
|
-
setLoading(true);
|
|
51
|
-
|
|
52
|
-
const _body = mergeBodies ? { ...body, ...customBody } : customBody || body;
|
|
53
|
-
const response = await query<T>({ url, body: _body, cache });
|
|
54
|
-
|
|
55
|
-
setLoading(false);
|
|
56
|
-
|
|
57
|
-
if (response.ok) {
|
|
58
|
-
setData(response.data);
|
|
59
|
-
} else {
|
|
60
|
-
setError(response.error);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
useEffectOnce(() => {
|
|
65
|
-
if (loadOnMount) {
|
|
66
|
-
load();
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
return { data, loading, load, error };
|
|
71
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { useContextSelector } from 'use-context-selector';
|
|
2
|
-
import { IReqraftContext, ReqraftContext } from '../contexts/ReqraftContext';
|
|
3
|
-
|
|
4
|
-
export const useReqraftProperty = <T extends keyof IReqraftContext>(
|
|
5
|
-
property: T
|
|
6
|
-
): IReqraftContext[T] => {
|
|
7
|
-
const contextProperty = useContextSelector(ReqraftContext, (value) => {
|
|
8
|
-
if (!(property in value)) {
|
|
9
|
-
throw new Error(`Reqraft context property ${property} not found`);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
return value[property];
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
return contextProperty;
|
|
16
|
-
};
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { ReqoreButton, ReqoreControlGroup, ReqoreP } from '@qoretechnologies/reqore';
|
|
2
|
-
import { StoryObj } from '@storybook/react';
|
|
3
|
-
import { fireEvent, within } from '@storybook/test';
|
|
4
|
-
import { storiesStorageMock } from '../../../__tests__/ mock';
|
|
5
|
-
import { testsWaitForText } from '../../../__tests__/utils';
|
|
6
|
-
import { StoryMeta } from '../../types';
|
|
7
|
-
import { useReqraftStorage } from './useStorage';
|
|
8
|
-
|
|
9
|
-
const meta = {
|
|
10
|
-
title: 'Hooks/useStorage',
|
|
11
|
-
render: () => {
|
|
12
|
-
const [storage, setStorage, removeValue] = useReqraftStorage<string>(
|
|
13
|
-
'some-path',
|
|
14
|
-
'This is a default value'
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<ReqoreControlGroup>
|
|
19
|
-
<ReqoreP>{storage}</ReqoreP>
|
|
20
|
-
<ReqoreButton onClick={() => setStorage('This is a NEW value')}>
|
|
21
|
-
Update storage
|
|
22
|
-
</ReqoreButton>
|
|
23
|
-
<ReqoreButton onClick={() => removeValue()}>Remove value</ReqoreButton>
|
|
24
|
-
</ReqoreControlGroup>
|
|
25
|
-
);
|
|
26
|
-
},
|
|
27
|
-
} as StoryMeta<any>;
|
|
28
|
-
|
|
29
|
-
export default meta;
|
|
30
|
-
export type Story = StoryObj<typeof meta>;
|
|
31
|
-
|
|
32
|
-
export const DefaultValue: Story = {
|
|
33
|
-
args: {
|
|
34
|
-
method: 'GET',
|
|
35
|
-
},
|
|
36
|
-
parameters: {
|
|
37
|
-
mockData: [
|
|
38
|
-
{
|
|
39
|
-
url: 'https://hq.qoretechnologies.com:8092/api/latest/users/_current_/storage',
|
|
40
|
-
method: 'GET',
|
|
41
|
-
status: 200,
|
|
42
|
-
response: {},
|
|
43
|
-
},
|
|
44
|
-
],
|
|
45
|
-
},
|
|
46
|
-
play: async () => {
|
|
47
|
-
await testsWaitForText('This is a default value');
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export const StorageValue: Story = {
|
|
52
|
-
parameters: {
|
|
53
|
-
mockData: [...storiesStorageMock],
|
|
54
|
-
},
|
|
55
|
-
play: async () => {
|
|
56
|
-
await testsWaitForText('This is a storage value');
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export const ValueCanBeUpdated: Story = {
|
|
61
|
-
...StorageValue,
|
|
62
|
-
play: async ({ canvasElement }) => {
|
|
63
|
-
const canvas = within(canvasElement);
|
|
64
|
-
|
|
65
|
-
//await sleep(1000);
|
|
66
|
-
|
|
67
|
-
await testsWaitForText('This is a storage value');
|
|
68
|
-
await testsWaitForText('Update storage');
|
|
69
|
-
await fireEvent.click(canvas.getByText('Update storage'));
|
|
70
|
-
await testsWaitForText('This is a NEW value');
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
export const ValueCanBeRemoved: Story = {
|
|
75
|
-
...StorageValue,
|
|
76
|
-
play: async ({ canvasElement }) => {
|
|
77
|
-
const canvas = within(canvasElement);
|
|
78
|
-
|
|
79
|
-
await testsWaitForText('This is a storage value');
|
|
80
|
-
await testsWaitForText('Remove value');
|
|
81
|
-
await fireEvent.click(canvas.getByText('Remove value'));
|
|
82
|
-
await testsWaitForText('This is a default value');
|
|
83
|
-
},
|
|
84
|
-
};
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { useContextSelector } from 'use-context-selector';
|
|
2
|
-
import { ReqraftStorageContext } from '../../contexts/StorageContext';
|
|
3
|
-
|
|
4
|
-
export type TReqraftStorageValue = string | number | boolean | Record<string | number, any> | any[];
|
|
5
|
-
export type TReqraftStorageHook<T extends TReqraftStorageValue> = [
|
|
6
|
-
T,
|
|
7
|
-
(newStorage: T) => void,
|
|
8
|
-
() => void,
|
|
9
|
-
];
|
|
10
|
-
|
|
11
|
-
export function useReqraftStorage<T extends TReqraftStorageValue>(
|
|
12
|
-
path: string,
|
|
13
|
-
defaultValue?: T,
|
|
14
|
-
includeAppPrefix?: boolean
|
|
15
|
-
): TReqraftStorageHook<T> {
|
|
16
|
-
const { getStorage, updateStorage, removeStorageValue } = useContextSelector(
|
|
17
|
-
ReqraftStorageContext,
|
|
18
|
-
({ getStorage, updateStorage, removeStorageValue }) => ({
|
|
19
|
-
getStorage,
|
|
20
|
-
updateStorage,
|
|
21
|
-
removeStorageValue,
|
|
22
|
-
})
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
return [
|
|
26
|
-
getStorage(path, defaultValue, includeAppPrefix),
|
|
27
|
-
(newStorage: T) => updateStorage(path, newStorage, includeAppPrefix),
|
|
28
|
-
() => removeStorageValue(path, includeAppPrefix),
|
|
29
|
-
];
|
|
30
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { TFormFieldType } from '../types/Form';
|
|
2
|
-
|
|
3
|
-
// @ts-expect-error "need to implement this"
|
|
4
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
5
|
-
export const useValidation = (value: any, type?: TFormFieldType) => {
|
|
6
|
-
// Build validation...
|
|
7
|
-
|
|
8
|
-
return true;
|
|
9
|
-
};
|
package/src/index.tsx
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export {
|
|
2
|
-
IReqraftMenuItem,
|
|
3
|
-
IReqraftMenuProps,
|
|
4
|
-
ReqraftMenu,
|
|
5
|
-
TReqraftMenu,
|
|
6
|
-
TReqraftMenuItem,
|
|
7
|
-
} from './components/menu/Menu';
|
|
8
|
-
|
|
9
|
-
export { FormStringField, IStringFormFieldProps } from './components/form/fields/string/String';
|
|
10
|
-
export { IReqraftUseFetch, useFetch } from './hooks/useFetch/useFetch';
|
|
11
|
-
export { ReqraftProvider, ReqraftQueryClient } from './providers/ReqraftProvider';
|
|
12
|
-
export { query } from './utils/fetch';
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { useQueryClient } from '@tanstack/react-query';
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
import { useEffectOnce } from 'react-use';
|
|
4
|
-
import { FetchContext, TReqraftContextQueryConfig } from '../contexts/FetchContext';
|
|
5
|
-
import { useReqraftProperty } from '../hooks/useReqraftProperty';
|
|
6
|
-
import { query, setupFetch } from '../utils/fetch';
|
|
7
|
-
|
|
8
|
-
export interface IReqraftFetchProviderProps {
|
|
9
|
-
children: React.ReactNode;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const ReqraftFetchProvider = ({ children }: IReqraftFetchProviderProps) => {
|
|
13
|
-
const queryClient = useQueryClient();
|
|
14
|
-
const instance = useReqraftProperty('instance');
|
|
15
|
-
const instanceToken = useReqraftProperty('instanceToken');
|
|
16
|
-
const instanceUnauthorizedRedirect = useReqraftProperty('instanceUnauthorizedRedirect');
|
|
17
|
-
|
|
18
|
-
const [ready, setReady] = useState(false);
|
|
19
|
-
|
|
20
|
-
useEffectOnce(() => {
|
|
21
|
-
setupFetch({
|
|
22
|
-
instance,
|
|
23
|
-
instanceToken,
|
|
24
|
-
unauthorizedRedirect: instanceUnauthorizedRedirect,
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
setReady(true);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
if (!ready) {
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async function get<T>(config: TReqraftContextQueryConfig) {
|
|
35
|
-
return query<T>({ queryClient, ...config, method: 'GET' });
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async function post<T>(config: TReqraftContextQueryConfig) {
|
|
39
|
-
return query<T>({ queryClient, ...config, method: 'POST' });
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function put<T>(config: TReqraftContextQueryConfig) {
|
|
43
|
-
return query<T>({ queryClient, ...config, method: 'PUT' });
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async function del<T>(config: TReqraftContextQueryConfig) {
|
|
47
|
-
return query<T>({ queryClient, ...config, method: 'DELETE' });
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<FetchContext.Provider
|
|
52
|
-
value={{
|
|
53
|
-
get,
|
|
54
|
-
post,
|
|
55
|
-
put,
|
|
56
|
-
del,
|
|
57
|
-
}}
|
|
58
|
-
>
|
|
59
|
-
{children}
|
|
60
|
-
</FetchContext.Provider>
|
|
61
|
-
);
|
|
62
|
-
};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
2
|
-
import { ReactNode } from 'react';
|
|
3
|
-
import { IReqraftContext, ReqraftContext } from '../contexts/ReqraftContext';
|
|
4
|
-
import { ReqraftFetchProvider } from './FetchProvider';
|
|
5
|
-
import { ReqraftStorageProvider } from './StorageProvider';
|
|
6
|
-
|
|
7
|
-
export const ReqraftQueryClient = new QueryClient();
|
|
8
|
-
|
|
9
|
-
export interface IReqraftProviderProps extends IReqraftContext {
|
|
10
|
-
children: ReactNode;
|
|
11
|
-
reactQueryClient?: QueryClient;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const ReqraftProvider = ({
|
|
15
|
-
appName,
|
|
16
|
-
children,
|
|
17
|
-
instance,
|
|
18
|
-
instanceToken,
|
|
19
|
-
instanceUnauthorizedRedirect,
|
|
20
|
-
reactQueryClient,
|
|
21
|
-
}: IReqraftProviderProps) => {
|
|
22
|
-
return (
|
|
23
|
-
<ReqraftContext.Provider
|
|
24
|
-
value={{ appName, instanceToken, instance, instanceUnauthorizedRedirect }}
|
|
25
|
-
>
|
|
26
|
-
<QueryClientProvider client={reactQueryClient || ReqraftQueryClient}>
|
|
27
|
-
<ReqraftFetchProvider>
|
|
28
|
-
<ReqraftStorageProvider>{children}</ReqraftStorageProvider>
|
|
29
|
-
</ReqraftFetchProvider>
|
|
30
|
-
</QueryClientProvider>
|
|
31
|
-
</ReqraftContext.Provider>
|
|
32
|
-
);
|
|
33
|
-
};
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { cloneDeep, get, set } from 'lodash';
|
|
2
|
-
import { ReactNode, useEffect, useState } from 'react';
|
|
3
|
-
import type { Get } from 'type-fest';
|
|
4
|
-
import { ReqraftStorageContext, TReqraftStorage } from '../contexts/StorageContext';
|
|
5
|
-
import { useFetch } from '../hooks/useFetch/useFetch';
|
|
6
|
-
import { useReqraftProperty } from '../hooks/useReqraftProperty';
|
|
7
|
-
import { TReqraftStorageValue } from '../hooks/useStorage/useStorage';
|
|
8
|
-
|
|
9
|
-
export interface IReqraftStorageProviderProps {
|
|
10
|
-
children: ReactNode;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export const ReqraftStorageProvider = ({ children }: IReqraftStorageProviderProps) => {
|
|
14
|
-
const appName = useReqraftProperty('appName');
|
|
15
|
-
|
|
16
|
-
const { data, loading } = useFetch({
|
|
17
|
-
url: 'users/_current_/storage',
|
|
18
|
-
cache: false,
|
|
19
|
-
loadOnMount: true,
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
const { load } = useFetch({
|
|
23
|
-
url: 'users/_current_/',
|
|
24
|
-
method: 'PUT',
|
|
25
|
-
cache: false,
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
const [storage, setStorage] = useState<TReqraftStorage>(data);
|
|
29
|
-
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
if (data) {
|
|
32
|
-
setStorage(data);
|
|
33
|
-
}
|
|
34
|
-
}, [data]);
|
|
35
|
-
|
|
36
|
-
const getStorage = function <T extends TReqraftStorageValue>(
|
|
37
|
-
path: string,
|
|
38
|
-
defaultValue: T,
|
|
39
|
-
includeAppPrefix: boolean = true
|
|
40
|
-
): Get<TReqraftStorage, string> {
|
|
41
|
-
const _path = includeAppPrefix ? `${appName}.${path}` : path;
|
|
42
|
-
|
|
43
|
-
return get(storage, _path) ?? defaultValue;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const updateStorage = function <T extends TReqraftStorageValue>(
|
|
47
|
-
path: string,
|
|
48
|
-
value: T,
|
|
49
|
-
includeAppPrefix: boolean = true
|
|
50
|
-
) {
|
|
51
|
-
const _path = includeAppPrefix ? `${appName}.${path}` : path;
|
|
52
|
-
const updatedStorage = set(cloneDeep(storage), _path, value);
|
|
53
|
-
|
|
54
|
-
setStorage(updatedStorage);
|
|
55
|
-
|
|
56
|
-
load({ body: { storage: updatedStorage } });
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const removeStorageValue = function (path: string, includeAppPrefix: boolean = true) {
|
|
60
|
-
const _path = includeAppPrefix ? `${appName}.${path}` : path;
|
|
61
|
-
|
|
62
|
-
const updatedStorage = set(cloneDeep(storage), _path, null);
|
|
63
|
-
|
|
64
|
-
setStorage(updatedStorage);
|
|
65
|
-
|
|
66
|
-
load({ body: { storage_path: _path } });
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
if (loading || !storage) {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return (
|
|
74
|
-
<ReqraftStorageContext.Provider
|
|
75
|
-
value={{ storage, getStorage, updateStorage, removeStorageValue }}
|
|
76
|
-
>
|
|
77
|
-
{children}
|
|
78
|
-
</ReqraftStorageContext.Provider>
|
|
79
|
-
);
|
|
80
|
-
};
|
package/src/types/Form.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
export type TFormFieldType =
|
|
2
|
-
| 'string'
|
|
3
|
-
| 'number'
|
|
4
|
-
| 'boolean'
|
|
5
|
-
| 'date'
|
|
6
|
-
| 'time'
|
|
7
|
-
| 'datetime'
|
|
8
|
-
| 'select'
|
|
9
|
-
| 'multiSelect'
|
|
10
|
-
| 'radio'
|
|
11
|
-
| 'checkbox'
|
|
12
|
-
| 'file'
|
|
13
|
-
| 'image'
|
|
14
|
-
| 'color'
|
|
15
|
-
| 'password'
|
|
16
|
-
| 'email'
|
|
17
|
-
| 'phone'
|
|
18
|
-
| 'url'
|
|
19
|
-
| 'markdown';
|
|
20
|
-
|
|
21
|
-
export type TFormFieldValueType<T> = T extends 'string'
|
|
22
|
-
? string
|
|
23
|
-
: T extends 'number'
|
|
24
|
-
? number
|
|
25
|
-
: T extends 'boolean'
|
|
26
|
-
? boolean
|
|
27
|
-
: T extends 'date'
|
|
28
|
-
? Date | string
|
|
29
|
-
: T extends 'time'
|
|
30
|
-
? Date | string
|
|
31
|
-
: T extends 'datetime'
|
|
32
|
-
? Date | string
|
|
33
|
-
: T extends 'select'
|
|
34
|
-
? string
|
|
35
|
-
: T extends 'multiSelect'
|
|
36
|
-
? string[]
|
|
37
|
-
: T extends 'radio'
|
|
38
|
-
? string
|
|
39
|
-
: T extends 'checkbox'
|
|
40
|
-
? boolean
|
|
41
|
-
: T extends 'file'
|
|
42
|
-
? File
|
|
43
|
-
: T extends 'image'
|
|
44
|
-
? string
|
|
45
|
-
: T extends 'color'
|
|
46
|
-
? string
|
|
47
|
-
: T extends 'password'
|
|
48
|
-
? string
|
|
49
|
-
: T extends 'email'
|
|
50
|
-
? string
|
|
51
|
-
: T extends 'phone'
|
|
52
|
-
? string
|
|
53
|
-
: T extends 'url'
|
|
54
|
-
? string
|
|
55
|
-
: T extends 'markdown'
|
|
56
|
-
? string
|
|
57
|
-
: any;
|
package/src/types.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { IReqoreUIProviderProps } from '@qoretechnologies/reqore/dist/containers/UIProvider';
|
|
2
|
-
import { Meta } from '@storybook/react';
|
|
3
|
-
|
|
4
|
-
export type StoryMeta<
|
|
5
|
-
Component extends keyof JSX.IntrinsicElements | React.JSXElementConstructor<any>,
|
|
6
|
-
AdditionalArgs = Record<string, any>
|
|
7
|
-
> = Meta<
|
|
8
|
-
React.ComponentProps<Component> &
|
|
9
|
-
AdditionalArgs & {
|
|
10
|
-
reqoreOptions: IReqoreUIProviderProps['options'];
|
|
11
|
-
}
|
|
12
|
-
>;
|
package/src/utils/fetch.ts
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { QueryClient } from '@tanstack/react-query';
|
|
2
|
-
import { ReqraftQueryClient } from '../providers/ReqraftProvider';
|
|
3
|
-
|
|
4
|
-
export interface IReqraftFetchConfig {
|
|
5
|
-
instance: string;
|
|
6
|
-
instanceToken: string;
|
|
7
|
-
unauthorizedRedirect?: (pathname: string) => string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface IReqraftFetchResponse<T> {
|
|
11
|
-
data: T;
|
|
12
|
-
ok: boolean;
|
|
13
|
-
code?: number;
|
|
14
|
-
error?: any;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const fetchConfig: IReqraftFetchConfig = {
|
|
18
|
-
instance: window.location.origin + '/',
|
|
19
|
-
instanceToken: '',
|
|
20
|
-
unauthorizedRedirect: (pathname: string) => `/?next=${pathname}`,
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const CACHE_EXPIRATION_TIME = 5 * 60 * 1000; // 5 minutes
|
|
24
|
-
|
|
25
|
-
export const setupFetch = ({
|
|
26
|
-
instance,
|
|
27
|
-
instanceToken,
|
|
28
|
-
unauthorizedRedirect,
|
|
29
|
-
}: IReqraftFetchConfig) => {
|
|
30
|
-
fetchConfig.instance = instance;
|
|
31
|
-
fetchConfig.instanceToken = instanceToken;
|
|
32
|
-
|
|
33
|
-
if (unauthorizedRedirect) {
|
|
34
|
-
fetchConfig.unauthorizedRedirect = unauthorizedRedirect;
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
async function doFetchData(
|
|
39
|
-
url: string,
|
|
40
|
-
method = 'GET',
|
|
41
|
-
body?: { [key: string]: any }
|
|
42
|
-
): Promise<Response> {
|
|
43
|
-
if (!fetchConfig.instanceToken) {
|
|
44
|
-
return new Response(JSON.stringify({}), {
|
|
45
|
-
status: 401,
|
|
46
|
-
statusText: 'Unauthorized',
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return fetch(`${fetchConfig.instance}api/latest/${url}`, {
|
|
51
|
-
method,
|
|
52
|
-
headers: {
|
|
53
|
-
'Content-Type': 'application/json',
|
|
54
|
-
Authorization: `Bearer ${fetchConfig.instanceToken}`,
|
|
55
|
-
},
|
|
56
|
-
body: JSON.stringify(body),
|
|
57
|
-
}).catch((error) => {
|
|
58
|
-
return new Response(JSON.stringify({}), {
|
|
59
|
-
status: 500,
|
|
60
|
-
statusText: `Request failed ${error.message}`,
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export interface IReqraftQueryConfig {
|
|
66
|
-
url: string;
|
|
67
|
-
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
68
|
-
body?: Record<string | number, any>;
|
|
69
|
-
cache?: boolean;
|
|
70
|
-
queryClient?: QueryClient;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export async function query<T>({
|
|
74
|
-
url,
|
|
75
|
-
method = 'GET',
|
|
76
|
-
body,
|
|
77
|
-
cache = true,
|
|
78
|
-
queryClient = ReqraftQueryClient,
|
|
79
|
-
}: IReqraftQueryConfig): Promise<IReqraftFetchResponse<T>> {
|
|
80
|
-
const shouldCache = method === 'DELETE' || method === 'POST' ? false : cache;
|
|
81
|
-
const cacheKey = `${url}:${method}:${JSON.stringify(body || {})}`;
|
|
82
|
-
|
|
83
|
-
const requestData = await queryClient.fetchQuery({
|
|
84
|
-
queryKey: [cacheKey],
|
|
85
|
-
queryFn: async () => {
|
|
86
|
-
const response = await doFetchData(url, method, body);
|
|
87
|
-
|
|
88
|
-
const clone = response.clone();
|
|
89
|
-
const json = await clone.json();
|
|
90
|
-
|
|
91
|
-
if (response.status === 401) {
|
|
92
|
-
window.location.href = fetchConfig.unauthorizedRedirect(window.location.pathname);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return {
|
|
96
|
-
data: json,
|
|
97
|
-
ok: response.ok,
|
|
98
|
-
status: response.status,
|
|
99
|
-
statusText: response.statusText,
|
|
100
|
-
};
|
|
101
|
-
},
|
|
102
|
-
staleTime: shouldCache ? CACHE_EXPIRATION_TIME : 0,
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
if (!requestData.ok) {
|
|
106
|
-
queryClient.invalidateQueries({ queryKey: [cacheKey] });
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
data: null,
|
|
110
|
-
ok: false,
|
|
111
|
-
code: requestData.status,
|
|
112
|
-
error: requestData.statusText,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return {
|
|
117
|
-
data: requestData.data,
|
|
118
|
-
ok: true,
|
|
119
|
-
code: requestData.status,
|
|
120
|
-
};
|
|
121
|
-
}
|