@cccsaurora/howler-ui 2.18.0-dev.841 → 2.19.0-cases.1040
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/api/action/index.d.ts +5 -4
- package/api/action/index.js +8 -8
- package/api/analytic/index.d.ts +3 -2
- package/api/analytic/index.js +4 -4
- package/api/analytic/owner.d.ts +2 -1
- package/api/analytic/owner.js +2 -2
- package/api/analytic/rules.d.ts +2 -1
- package/api/analytic/rules.js +2 -2
- package/api/dossier/index.d.ts +4 -3
- package/api/dossier/index.js +6 -6
- package/api/hit/index.d.ts +3 -2
- package/api/hit/index.js +4 -4
- package/api/hit/labels.d.ts +3 -2
- package/api/hit/labels.js +4 -4
- package/api/hit/overwrite.d.ts +2 -1
- package/api/hit/overwrite.js +2 -2
- package/api/hit/transition.d.ts +2 -1
- package/api/hit/transition.js +2 -2
- package/api/index.d.ts +5 -4
- package/api/index.js +8 -8
- package/api/overview/index.d.ts +4 -3
- package/api/overview/index.js +6 -6
- package/api/search/overview.d.ts +2 -2
- package/api/template/index.d.ts +4 -3
- package/api/template/index.js +6 -6
- package/api/user/index.d.ts +2 -1
- package/api/user/index.js +2 -2
- package/api/view/index.d.ts +4 -3
- package/api/view/index.js +6 -6
- package/components/app/App.d.ts +1 -0
- package/components/app/App.js +24 -6
- package/components/app/providers/FavouritesProvider.js +0 -4
- package/components/app/providers/ModalProvider.d.ts +1 -0
- package/components/app/providers/ModalProvider.js +5 -1
- package/components/app/providers/RecordProvider.d.ts +1 -1
- package/components/app/providers/RecordProvider.js +1 -1
- package/components/app/providers/SearchResponseProvider.d.ts +21 -0
- package/components/app/providers/SearchResponseProvider.js +81 -0
- package/components/app/providers/SearchResponseProvider.test.d.ts +1 -0
- package/components/app/providers/SearchResponseProvider.test.js +406 -0
- package/components/app/providers/ViewProvider.test.js +4 -1
- package/components/elements/addons/buttons/CustomButton.test.d.ts +1 -0
- package/components/elements/addons/buttons/CustomButton.test.js +106 -0
- package/components/elements/addons/layout/FlexOne.test.d.ts +1 -0
- package/components/elements/addons/layout/FlexOne.test.js +30 -0
- package/components/elements/addons/lists/TuiListBase.js +4 -0
- package/components/elements/addons/lists/TuiListElement.js +6 -3
- package/components/elements/addons/lists/index.d.ts +1 -0
- package/components/elements/addons/search/SearchPagination.d.ts +2 -1
- package/components/elements/addons/search/SearchPagination.js +4 -4
- package/components/elements/addons/search/SearchPagination.test.d.ts +1 -0
- package/components/elements/addons/search/SearchPagination.test.js +80 -0
- package/components/elements/addons/search/SearchTotal.test.d.ts +1 -0
- package/components/elements/addons/search/SearchTotal.test.js +47 -0
- package/components/elements/display/DocumentationButton.test.d.ts +1 -0
- package/components/elements/display/DocumentationButton.test.js +84 -0
- package/components/elements/display/DynamicTabs.test.d.ts +1 -0
- package/components/elements/display/DynamicTabs.test.js +93 -0
- package/components/elements/display/HowlerAvatar.js +2 -1
- package/components/elements/display/HowlerCard.test.d.ts +1 -0
- package/components/elements/display/HowlerCard.test.js +51 -0
- package/components/elements/display/Image.test.d.ts +1 -0
- package/components/elements/display/Image.test.js +79 -0
- package/components/elements/display/ItemManager.d.ts +2 -2
- package/components/elements/display/ItemManager.js +1 -1
- package/components/elements/display/Markdown.js +9 -2
- package/components/elements/display/QueryResultText.test.d.ts +1 -0
- package/components/elements/display/QueryResultText.test.js +48 -0
- package/components/elements/display/TextDivider.test.d.ts +1 -0
- package/components/elements/display/TextDivider.test.js +43 -0
- package/components/elements/display/TypingIndicator.test.d.ts +1 -0
- package/components/elements/display/TypingIndicator.test.js +33 -0
- package/components/elements/display/modals/ConfirmDeleteModal.d.ts +4 -0
- package/components/elements/display/modals/ConfirmDeleteModal.js +4 -2
- package/components/elements/hit/HitBanner.js +6 -3
- package/components/elements/hit/HitLinks.js +1 -1
- package/components/elements/hit/HitSummary.js +3 -2
- package/components/elements/hit/elements/AnalyticLink.js +1 -1
- package/components/elements/hit/elements/Assigned.d.ts +2 -0
- package/components/elements/hit/elements/Assigned.js +15 -13
- package/components/elements/record/RecordContextMenu.js +15 -3
- package/components/elements/record/RecordContextMenu.test.js +96 -0
- package/components/hooks/useMyApi.d.ts +1 -1
- package/components/hooks/useMyPreferences.js +2 -1
- package/components/routes/403.d.ts +3 -0
- package/components/routes/403.js +10 -0
- package/components/routes/403.test.d.ts +1 -0
- package/components/routes/403.test.js +27 -0
- package/components/routes/404.test.d.ts +1 -0
- package/components/routes/404.test.js +26 -0
- package/components/routes/ErrorBoundary.test.d.ts +1 -0
- package/components/routes/ErrorBoundary.test.js +43 -0
- package/components/routes/ErrorOccured.test.d.ts +1 -0
- package/components/routes/ErrorOccured.test.js +35 -0
- package/components/routes/Logout.test.d.ts +1 -0
- package/components/routes/Logout.test.js +59 -0
- package/components/routes/action/edit/ActionEditor.js +1 -1
- package/components/routes/action/useMyActionFunctions.js +30 -6
- package/components/routes/action/view/ActionDetails.js +15 -2
- package/components/routes/action/view/ActionSearch.js +32 -20
- package/components/routes/action/view/markdown/integrations.en.md.js +1 -1
- package/components/routes/action/view/markdown/integrations.fr.md.js +1 -1
- package/components/routes/analytics/AnalyticDetails.js +10 -6
- package/components/routes/analytics/AnalyticSearch.js +6 -6
- package/components/routes/cases/Cases.js +5 -6
- package/components/routes/cases/modals/ResolveModal.js +14 -13
- package/components/routes/cases/modals/ResolveModal.test.js +85 -25
- package/components/routes/dossiers/Dossiers.js +25 -19
- package/components/routes/help/ActionIntroductionDocumentation.js +1 -1
- package/components/routes/help/markdown/en/client.md.js +1 -1
- package/components/routes/help/markdown/en/retention.md.js +1 -1
- package/components/routes/help/markdown/fr/client.md.js +1 -1
- package/components/routes/help/markdown/fr/retention.md.js +1 -1
- package/components/routes/hits/search/shared/CustomSpan.js +7 -7
- package/components/routes/hits/search/shared/CustomSpan.test.d.ts +1 -0
- package/components/routes/hits/search/shared/CustomSpan.test.js +170 -0
- package/components/routes/home/AddNewCard.js +1 -1
- package/components/routes/home/AddNewCard.test.d.ts +1 -0
- package/components/routes/home/AddNewCard.test.js +86 -0
- package/components/routes/home/ViewCard.js +1 -1
- package/components/routes/home/ViewRefresh.test.d.ts +1 -0
- package/components/routes/home/ViewRefresh.test.js +79 -0
- package/components/routes/overviews/OverviewCard.test.d.ts +1 -0
- package/components/routes/overviews/OverviewCard.test.js +108 -0
- package/components/routes/overviews/OverviewViewer.js +16 -8
- package/components/routes/overviews/Overviews.js +25 -19
- package/components/routes/overviews/template/en.md.js +1 -1
- package/components/routes/overviews/template/fr.md.js +1 -1
- package/components/routes/settings/LocalSection.js +2 -1
- package/components/routes/settings/SettingsSection.test.d.ts +1 -0
- package/components/routes/settings/SettingsSection.test.js +19 -0
- package/components/routes/templates/TemplateCard.d.ts +3 -1
- package/components/routes/templates/TemplateCard.js +8 -4
- package/components/routes/templates/TemplateCard.test.d.ts +1 -0
- package/components/routes/templates/TemplateCard.test.js +115 -0
- package/components/routes/templates/TemplateViewer.js +62 -16
- package/components/routes/templates/Templates.js +35 -13
- package/components/routes/views/Views.js +21 -14
- package/locales/en/translation.json +25 -4
- package/locales/fr/translation.json +25 -4
- package/models/entities/generated/ApiType.d.ts +1 -1
- package/models/entities/generated/Hit.d.ts +1 -0
- package/models/entities/generated/Howler.d.ts +1 -0
- package/models/entities/generated/UserUser.d.ts +2 -0
- package/package.json +143 -148
- package/rest/FetchClient.test.d.ts +1 -0
- package/rest/FetchClient.test.js +81 -0
- package/setupTests.js +5 -0
- package/utils/Throttler.test.d.ts +1 -0
- package/utils/Throttler.test.js +135 -0
- package/utils/actionUtils.test.d.ts +1 -0
- package/utils/actionUtils.test.js +140 -0
- package/utils/constants.d.ts +1 -0
- package/utils/constants.js +1 -0
- package/utils/hitFunctions.test.d.ts +1 -0
- package/utils/hitFunctions.test.js +52 -0
- package/utils/localStorage.test.d.ts +1 -0
- package/utils/localStorage.test.js +96 -0
- package/utils/menuUtils.js +1 -1
- package/utils/menuUtils.test.d.ts +1 -0
- package/utils/menuUtils.test.js +198 -0
- package/utils/sessionStorage.test.d.ts +1 -0
- package/utils/sessionStorage.test.js +81 -0
- package/utils/stringUtils.test.d.ts +1 -0
- package/utils/stringUtils.test.js +159 -0
- package/utils/utils.test.d.ts +1 -0
- package/utils/utils.test.js +292 -0
- package/utils/viewUtils.test.d.ts +1 -0
- package/utils/viewUtils.test.js +45 -0
- package/utils/xsrf.test.d.ts +1 -0
- package/utils/xsrf.test.js +41 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { HowlerSearchRequest, HowlerSearchResponse } from '@cccsaurora/howler-ui/api/search';
|
|
2
|
+
import type { DispatchApiConfig } from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
3
|
+
import { type PropsWithChildren } from 'react';
|
|
4
|
+
export type SearchResponseState<T> = HowlerSearchResponse<T> & {
|
|
5
|
+
removeCount: number;
|
|
6
|
+
};
|
|
7
|
+
export type SearchResponseContextType<T> = {
|
|
8
|
+
push: (item: T) => void;
|
|
9
|
+
remove: (id: string) => void;
|
|
10
|
+
replace: (id: string, item: T) => void;
|
|
11
|
+
request: (endpoint: (request: HowlerSearchRequest) => Promise<HowlerSearchResponse<T>>, requestData: HowlerSearchRequest, config?: DispatchApiConfig) => Promise<HowlerSearchResponse<T>>;
|
|
12
|
+
getSearchRequestData: (requestData: Partial<HowlerSearchRequest>) => Partial<HowlerSearchRequest>;
|
|
13
|
+
response: SearchResponseState<T> | null;
|
|
14
|
+
};
|
|
15
|
+
export declare const SearchResponseContext: import("react").Context<SearchResponseContextType<any>>;
|
|
16
|
+
type SearchResponseProviderProps<T> = PropsWithChildren<{
|
|
17
|
+
idField: string;
|
|
18
|
+
initialResponse?: SearchResponseState<T>;
|
|
19
|
+
}>;
|
|
20
|
+
declare const SearchResponseProvider: <T>({ children, idField: idField, initialResponse }: SearchResponseProviderProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
export default SearchResponseProvider;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
3
|
+
import { createContext, useCallback, useState } from 'react';
|
|
4
|
+
export const SearchResponseContext = createContext(null);
|
|
5
|
+
const SearchResponseProvider = ({ children, idField: idField, initialResponse = null }) => {
|
|
6
|
+
const { dispatchApi } = useMyApi();
|
|
7
|
+
const [response, setResponse] = useState(initialResponse);
|
|
8
|
+
const request = useCallback(async (endpoint, requestData, config) => {
|
|
9
|
+
const _response = await dispatchApi(endpoint(requestData), config);
|
|
10
|
+
setResponse(_prevResponse => ({
|
|
11
|
+
..._response,
|
|
12
|
+
removeCount: _response.offset <= _prevResponse?.offset ? 0 : (_prevResponse?.removeCount ?? 0)
|
|
13
|
+
}));
|
|
14
|
+
return _response;
|
|
15
|
+
}, [dispatchApi]);
|
|
16
|
+
const getSearchRequestData = useCallback((requestData) => {
|
|
17
|
+
const modifiedRequest = { ...requestData };
|
|
18
|
+
if (response?.removeCount) {
|
|
19
|
+
if (response.offset < modifiedRequest.offset) {
|
|
20
|
+
modifiedRequest.offset = Math.max(0, modifiedRequest.offset - response.removeCount);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return modifiedRequest;
|
|
24
|
+
}, [response]);
|
|
25
|
+
const push = useCallback((item) => {
|
|
26
|
+
setResponse(_response => {
|
|
27
|
+
if (_response === null) {
|
|
28
|
+
return _response;
|
|
29
|
+
}
|
|
30
|
+
const filteredItems = _response.items.filter(v => v[idField] !== item[idField]);
|
|
31
|
+
const itemExists = filteredItems.length < _response.items.length;
|
|
32
|
+
filteredItems.push(item);
|
|
33
|
+
return {
|
|
34
|
+
items: filteredItems.length <= _response.rows ? filteredItems : _response.items,
|
|
35
|
+
offset: _response.offset,
|
|
36
|
+
rows: _response.rows,
|
|
37
|
+
total: itemExists ? _response.total : _response.total + 1,
|
|
38
|
+
removeCount: itemExists ? _response.removeCount : Math.max(_response.removeCount - 1, 0)
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
}, [idField]);
|
|
42
|
+
const replace = useCallback((id, item) => {
|
|
43
|
+
if (item[idField] !== undefined && id !== item[idField]) {
|
|
44
|
+
throw new Error('Item id is defined but id does not match the id provided to replace function');
|
|
45
|
+
}
|
|
46
|
+
const newItem = {
|
|
47
|
+
...item,
|
|
48
|
+
[idField]: item[idField] !== undefined ? item[idField] : id
|
|
49
|
+
};
|
|
50
|
+
setResponse(_response => {
|
|
51
|
+
if (_response === null) {
|
|
52
|
+
return _response;
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
items: _response.items.map(v => (v[idField] === id ? newItem : v)),
|
|
56
|
+
offset: _response.offset,
|
|
57
|
+
rows: _response.rows,
|
|
58
|
+
total: _response.total,
|
|
59
|
+
removeCount: _response.removeCount
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
}, [idField]);
|
|
63
|
+
const remove = useCallback((id) => {
|
|
64
|
+
setResponse(_response => {
|
|
65
|
+
if (_response === null) {
|
|
66
|
+
return _response;
|
|
67
|
+
}
|
|
68
|
+
const filteredItems = _response.items.filter(v => v[idField] !== id);
|
|
69
|
+
const itemExists = filteredItems.length < _response.items.length;
|
|
70
|
+
return {
|
|
71
|
+
items: filteredItems,
|
|
72
|
+
offset: _response.offset,
|
|
73
|
+
rows: _response.rows,
|
|
74
|
+
total: itemExists ? _response.total - 1 : _response.total,
|
|
75
|
+
removeCount: itemExists ? _response.removeCount + 1 : _response.removeCount
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
}, [idField]);
|
|
79
|
+
return (_jsx(SearchResponseContext.Provider, { value: { push, remove, replace, request, getSearchRequestData, response }, children: children }));
|
|
80
|
+
};
|
|
81
|
+
export default SearchResponseProvider;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { act, renderHook } from '@testing-library/react';
|
|
3
|
+
import { useContext } from 'react';
|
|
4
|
+
import SearchResponseProvider, { SearchResponseContext } from './SearchResponseProvider';
|
|
5
|
+
const TEST_PAGE_SIZE = 25;
|
|
6
|
+
const TEST_TOTAL_COUNT = 100;
|
|
7
|
+
const makeWrapper = (initialResponse) => {
|
|
8
|
+
const Wrapper = ({ children }) => (_jsx(SearchResponseProvider, { idField: "id", initialResponse: initialResponse, children: children }));
|
|
9
|
+
return Wrapper;
|
|
10
|
+
};
|
|
11
|
+
const renderProvider = (initialResponse) => {
|
|
12
|
+
return renderHook(() => useContext(SearchResponseContext), {
|
|
13
|
+
wrapper: makeWrapper(initialResponse)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
describe('push', () => {
|
|
17
|
+
it("should add an item to the response if it doesn't exist", () => {
|
|
18
|
+
const hook = renderProvider({
|
|
19
|
+
items: [{ id: '0', name: 'existing' }],
|
|
20
|
+
offset: 0,
|
|
21
|
+
rows: TEST_PAGE_SIZE,
|
|
22
|
+
total: TEST_TOTAL_COUNT,
|
|
23
|
+
removeCount: 0
|
|
24
|
+
});
|
|
25
|
+
act(() => {
|
|
26
|
+
hook.result.current.push({ id: '1', name: 'test' });
|
|
27
|
+
});
|
|
28
|
+
expect(hook.result.current.response).toEqual({
|
|
29
|
+
items: [
|
|
30
|
+
{ id: '0', name: 'existing' },
|
|
31
|
+
{ id: '1', name: 'test' }
|
|
32
|
+
],
|
|
33
|
+
offset: 0,
|
|
34
|
+
rows: TEST_PAGE_SIZE,
|
|
35
|
+
total: TEST_TOTAL_COUNT + 1,
|
|
36
|
+
removeCount: 0
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
it('should replace an item in the response if it already exists', () => {
|
|
40
|
+
const hook = renderProvider({
|
|
41
|
+
items: [{ id: '0', name: 'old' }],
|
|
42
|
+
offset: 0,
|
|
43
|
+
rows: TEST_PAGE_SIZE,
|
|
44
|
+
total: TEST_TOTAL_COUNT,
|
|
45
|
+
removeCount: 0
|
|
46
|
+
});
|
|
47
|
+
act(() => {
|
|
48
|
+
hook.result.current.push({ id: '0', name: 'new' });
|
|
49
|
+
});
|
|
50
|
+
expect(hook.result.current.response).toEqual({
|
|
51
|
+
items: [{ id: '0', name: 'new' }],
|
|
52
|
+
offset: 0,
|
|
53
|
+
rows: TEST_PAGE_SIZE,
|
|
54
|
+
total: TEST_TOTAL_COUNT,
|
|
55
|
+
removeCount: 0
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
it('should update total but not add item if the response has the maximum number of items', () => {
|
|
59
|
+
const items = Array.from({ length: TEST_PAGE_SIZE }, (_, i) => ({ id: i.toString(), name: `item${i}` }));
|
|
60
|
+
const newItem = { id: TEST_PAGE_SIZE.toString(), name: 'test' };
|
|
61
|
+
const hook = renderProvider({
|
|
62
|
+
items,
|
|
63
|
+
offset: 0,
|
|
64
|
+
rows: TEST_PAGE_SIZE,
|
|
65
|
+
total: TEST_TOTAL_COUNT,
|
|
66
|
+
removeCount: 0
|
|
67
|
+
});
|
|
68
|
+
act(() => {
|
|
69
|
+
hook.result.current.push(newItem);
|
|
70
|
+
});
|
|
71
|
+
expect(hook.result.current.response?.items).not.toContainEqual(newItem);
|
|
72
|
+
expect(hook.result.current.response).toEqual({
|
|
73
|
+
items,
|
|
74
|
+
offset: 0,
|
|
75
|
+
rows: TEST_PAGE_SIZE,
|
|
76
|
+
total: TEST_TOTAL_COUNT + 1,
|
|
77
|
+
removeCount: 0
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
it('should decrement removeCount if the item does not exist already', () => {
|
|
81
|
+
const hook = renderProvider({
|
|
82
|
+
items: [{ id: '0', name: 'existing' }],
|
|
83
|
+
offset: 0,
|
|
84
|
+
rows: TEST_PAGE_SIZE,
|
|
85
|
+
total: TEST_TOTAL_COUNT,
|
|
86
|
+
removeCount: 1
|
|
87
|
+
});
|
|
88
|
+
act(() => {
|
|
89
|
+
hook.result.current.push({ id: '1', name: 'test' });
|
|
90
|
+
});
|
|
91
|
+
expect(hook.result.current.response).not.toBeNull();
|
|
92
|
+
expect(hook.result.current.response.removeCount).toBe(0);
|
|
93
|
+
});
|
|
94
|
+
it('should not decrement removeCount if the item already exists', () => {
|
|
95
|
+
const hook = renderProvider({
|
|
96
|
+
items: [{ id: '0', name: 'existing' }],
|
|
97
|
+
offset: 0,
|
|
98
|
+
rows: TEST_PAGE_SIZE,
|
|
99
|
+
total: TEST_TOTAL_COUNT,
|
|
100
|
+
removeCount: 1
|
|
101
|
+
});
|
|
102
|
+
act(() => {
|
|
103
|
+
hook.result.current.push({ id: '0', name: 'new' });
|
|
104
|
+
});
|
|
105
|
+
expect(hook.result.current.response).not.toBeNull();
|
|
106
|
+
expect(hook.result.current.response.removeCount).toBe(1);
|
|
107
|
+
});
|
|
108
|
+
it('should keep response null if it is uninitiated', () => {
|
|
109
|
+
const hook = renderProvider();
|
|
110
|
+
act(() => {
|
|
111
|
+
hook.result.current.push({ id: '0', name: 'test' });
|
|
112
|
+
});
|
|
113
|
+
expect(hook.result.current.response).toBeNull();
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
describe('remove', () => {
|
|
117
|
+
it('should remove an item from the response if it exists', () => {
|
|
118
|
+
const hook = renderProvider({
|
|
119
|
+
items: [
|
|
120
|
+
{ id: '0', name: 'item0' },
|
|
121
|
+
{ id: '1', name: 'item1' }
|
|
122
|
+
],
|
|
123
|
+
offset: 0,
|
|
124
|
+
rows: TEST_PAGE_SIZE,
|
|
125
|
+
total: TEST_TOTAL_COUNT,
|
|
126
|
+
removeCount: 0
|
|
127
|
+
});
|
|
128
|
+
act(() => {
|
|
129
|
+
hook.result.current.remove('0');
|
|
130
|
+
});
|
|
131
|
+
expect(hook.result.current.response).toEqual({
|
|
132
|
+
items: [{ id: '1', name: 'item1' }],
|
|
133
|
+
offset: 0,
|
|
134
|
+
rows: TEST_PAGE_SIZE,
|
|
135
|
+
total: TEST_TOTAL_COUNT - 1,
|
|
136
|
+
removeCount: 1
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
it('should ignore the remove if the item does not exist', () => {
|
|
140
|
+
const hook = renderProvider({
|
|
141
|
+
items: [{ id: '0', name: 'item' }],
|
|
142
|
+
offset: 0,
|
|
143
|
+
rows: TEST_PAGE_SIZE,
|
|
144
|
+
total: TEST_TOTAL_COUNT,
|
|
145
|
+
removeCount: 0
|
|
146
|
+
});
|
|
147
|
+
act(() => {
|
|
148
|
+
hook.result.current.remove('1');
|
|
149
|
+
});
|
|
150
|
+
expect(hook.result.current.response).toEqual({
|
|
151
|
+
items: [{ id: '0', name: 'item' }],
|
|
152
|
+
offset: 0,
|
|
153
|
+
rows: TEST_PAGE_SIZE,
|
|
154
|
+
total: TEST_TOTAL_COUNT,
|
|
155
|
+
removeCount: 0
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
it('should keep response null if it is uninitiated', () => {
|
|
159
|
+
const hook = renderProvider();
|
|
160
|
+
act(() => {
|
|
161
|
+
hook.result.current.remove('0');
|
|
162
|
+
});
|
|
163
|
+
expect(hook.result.current.response).toBeNull();
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
describe('replace', () => {
|
|
167
|
+
it('should replace an item in the response if it exists', () => {
|
|
168
|
+
const hook = renderProvider({
|
|
169
|
+
items: [{ id: '0', name: 'old' }],
|
|
170
|
+
offset: 0,
|
|
171
|
+
rows: TEST_PAGE_SIZE,
|
|
172
|
+
total: TEST_TOTAL_COUNT,
|
|
173
|
+
removeCount: 0
|
|
174
|
+
});
|
|
175
|
+
act(() => {
|
|
176
|
+
hook.result.current.replace('0', { id: undefined, name: 'new' });
|
|
177
|
+
});
|
|
178
|
+
expect(hook.result.current.response).toEqual({
|
|
179
|
+
items: [{ id: '0', name: 'new' }],
|
|
180
|
+
offset: 0,
|
|
181
|
+
rows: TEST_PAGE_SIZE,
|
|
182
|
+
total: TEST_TOTAL_COUNT,
|
|
183
|
+
removeCount: 0
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
it('should ignore the replace if the item does not exist', () => {
|
|
187
|
+
const hook = renderProvider({
|
|
188
|
+
items: [{ id: '0', name: 'item' }],
|
|
189
|
+
offset: 0,
|
|
190
|
+
rows: TEST_PAGE_SIZE,
|
|
191
|
+
total: TEST_TOTAL_COUNT,
|
|
192
|
+
removeCount: 0
|
|
193
|
+
});
|
|
194
|
+
act(() => {
|
|
195
|
+
hook.result.current.replace('1', { id: undefined, name: 'new' });
|
|
196
|
+
});
|
|
197
|
+
expect(hook.result.current.response).not.toContainEqual({ id: '1', name: 'new' });
|
|
198
|
+
expect(hook.result.current.response).toEqual({
|
|
199
|
+
items: [{ id: '0', name: 'item' }],
|
|
200
|
+
offset: 0,
|
|
201
|
+
rows: TEST_PAGE_SIZE,
|
|
202
|
+
total: TEST_TOTAL_COUNT,
|
|
203
|
+
removeCount: 0
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
it('should keep response null if it is uninitiated', () => {
|
|
207
|
+
const hook = renderProvider();
|
|
208
|
+
act(() => {
|
|
209
|
+
hook.result.current.replace('0', { id: '0', name: 'new' });
|
|
210
|
+
});
|
|
211
|
+
expect(hook.result.current.response).toBeNull();
|
|
212
|
+
});
|
|
213
|
+
it('should throw error if item id does not match the id provided', () => {
|
|
214
|
+
const hook = renderProvider({
|
|
215
|
+
items: [{ id: '0', name: 'old' }],
|
|
216
|
+
offset: 0,
|
|
217
|
+
rows: TEST_PAGE_SIZE,
|
|
218
|
+
total: TEST_TOTAL_COUNT,
|
|
219
|
+
removeCount: 0
|
|
220
|
+
});
|
|
221
|
+
expect(() => act(() => {
|
|
222
|
+
hook.result.current.replace('0', { id: '1', name: 'new' });
|
|
223
|
+
})).toThrow(/id does not match/);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
describe('request', () => {
|
|
227
|
+
const apiSearchMock = vi.fn();
|
|
228
|
+
beforeEach(() => {
|
|
229
|
+
apiSearchMock.mockReset();
|
|
230
|
+
});
|
|
231
|
+
it('should update the response with the result of the request', async () => {
|
|
232
|
+
const hook = renderProvider({
|
|
233
|
+
items: [{ id: '0', name: 'item' }],
|
|
234
|
+
offset: 0,
|
|
235
|
+
rows: TEST_PAGE_SIZE,
|
|
236
|
+
total: TEST_TOTAL_COUNT,
|
|
237
|
+
removeCount: 0
|
|
238
|
+
});
|
|
239
|
+
const request = {
|
|
240
|
+
query: 'test',
|
|
241
|
+
rows: TEST_PAGE_SIZE,
|
|
242
|
+
offset: 0
|
|
243
|
+
};
|
|
244
|
+
apiSearchMock.mockResolvedValue({
|
|
245
|
+
items: [{ id: '1', name: 'new' }],
|
|
246
|
+
offset: request.offset,
|
|
247
|
+
rows: TEST_PAGE_SIZE,
|
|
248
|
+
total: TEST_TOTAL_COUNT
|
|
249
|
+
});
|
|
250
|
+
await act(async () => {
|
|
251
|
+
await hook.result.current.request(apiSearchMock, request);
|
|
252
|
+
});
|
|
253
|
+
expect(apiSearchMock).toHaveBeenCalledWith(request);
|
|
254
|
+
expect(hook.result.current.response).toEqual({
|
|
255
|
+
items: [{ id: '1', name: 'new' }],
|
|
256
|
+
offset: request.offset,
|
|
257
|
+
rows: TEST_PAGE_SIZE,
|
|
258
|
+
total: TEST_TOTAL_COUNT,
|
|
259
|
+
removeCount: 0
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
it.for([
|
|
263
|
+
{ description: 'before', offset: 0 },
|
|
264
|
+
{ description: 'the same as', offset: TEST_PAGE_SIZE }
|
|
265
|
+
])('should reset removeCount if the offset is $description the current offset', async ({ offset }) => {
|
|
266
|
+
const hook = renderProvider({
|
|
267
|
+
items: [{ id: '0', name: 'item' }],
|
|
268
|
+
offset: TEST_PAGE_SIZE,
|
|
269
|
+
rows: TEST_PAGE_SIZE,
|
|
270
|
+
total: TEST_TOTAL_COUNT,
|
|
271
|
+
removeCount: 5
|
|
272
|
+
});
|
|
273
|
+
const request = {
|
|
274
|
+
query: 'test',
|
|
275
|
+
rows: TEST_PAGE_SIZE,
|
|
276
|
+
offset: offset
|
|
277
|
+
};
|
|
278
|
+
apiSearchMock.mockResolvedValue({
|
|
279
|
+
items: [{ id: '1', name: 'new' }],
|
|
280
|
+
offset: request.offset,
|
|
281
|
+
rows: TEST_PAGE_SIZE,
|
|
282
|
+
total: TEST_TOTAL_COUNT
|
|
283
|
+
});
|
|
284
|
+
await act(async () => {
|
|
285
|
+
await hook.result.current.request(apiSearchMock, request);
|
|
286
|
+
});
|
|
287
|
+
expect(apiSearchMock).toHaveBeenCalledWith(request);
|
|
288
|
+
expect(hook.result.current.response).toEqual({
|
|
289
|
+
items: [{ id: '1', name: 'new' }],
|
|
290
|
+
offset: request.offset,
|
|
291
|
+
rows: TEST_PAGE_SIZE,
|
|
292
|
+
total: TEST_TOTAL_COUNT,
|
|
293
|
+
removeCount: 0
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
it('should not reset removeCount if the offset is after the current offset', async () => {
|
|
297
|
+
const hook = renderProvider({
|
|
298
|
+
items: [{ id: '0', name: 'item' }],
|
|
299
|
+
offset: TEST_PAGE_SIZE,
|
|
300
|
+
rows: TEST_PAGE_SIZE,
|
|
301
|
+
total: TEST_TOTAL_COUNT,
|
|
302
|
+
removeCount: 5
|
|
303
|
+
});
|
|
304
|
+
const request = {
|
|
305
|
+
query: 'test',
|
|
306
|
+
rows: TEST_PAGE_SIZE,
|
|
307
|
+
offset: TEST_PAGE_SIZE * 2
|
|
308
|
+
};
|
|
309
|
+
apiSearchMock.mockResolvedValue({
|
|
310
|
+
items: [{ id: '1', name: 'new' }],
|
|
311
|
+
offset: request.offset,
|
|
312
|
+
rows: TEST_PAGE_SIZE,
|
|
313
|
+
total: TEST_TOTAL_COUNT
|
|
314
|
+
});
|
|
315
|
+
await act(async () => {
|
|
316
|
+
await hook.result.current.request(apiSearchMock, request);
|
|
317
|
+
});
|
|
318
|
+
expect(apiSearchMock).toHaveBeenCalledWith(request);
|
|
319
|
+
expect(hook.result.current.response).toEqual({
|
|
320
|
+
items: [{ id: '1', name: 'new' }],
|
|
321
|
+
offset: request.offset,
|
|
322
|
+
rows: TEST_PAGE_SIZE,
|
|
323
|
+
total: TEST_TOTAL_COUNT,
|
|
324
|
+
removeCount: 5
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
it('should keep response undefined if the request fails', async () => {
|
|
328
|
+
const hook = renderProvider();
|
|
329
|
+
const request = {
|
|
330
|
+
query: 'test',
|
|
331
|
+
rows: TEST_PAGE_SIZE,
|
|
332
|
+
offset: 0
|
|
333
|
+
};
|
|
334
|
+
apiSearchMock.mockRejectedValue(new Error('Request failed'));
|
|
335
|
+
await act(async () => {
|
|
336
|
+
await hook.result.current.request(apiSearchMock, request).catch(() => { });
|
|
337
|
+
});
|
|
338
|
+
expect(apiSearchMock).toHaveBeenCalledWith(request);
|
|
339
|
+
expect(hook.result.current.response).toBeNull();
|
|
340
|
+
});
|
|
341
|
+
it('should keep response unchanged if the request fails', async () => {
|
|
342
|
+
const initialResponse = {
|
|
343
|
+
items: [{ id: '0', name: 'item' }],
|
|
344
|
+
offset: 0,
|
|
345
|
+
rows: TEST_PAGE_SIZE,
|
|
346
|
+
total: TEST_TOTAL_COUNT,
|
|
347
|
+
removeCount: 5
|
|
348
|
+
};
|
|
349
|
+
const hook = renderProvider(initialResponse);
|
|
350
|
+
const request = {
|
|
351
|
+
query: 'test',
|
|
352
|
+
rows: TEST_PAGE_SIZE,
|
|
353
|
+
offset: 0
|
|
354
|
+
};
|
|
355
|
+
apiSearchMock.mockRejectedValue(new Error('Request failed'));
|
|
356
|
+
await act(async () => {
|
|
357
|
+
await hook.result.current.request(apiSearchMock, request).catch(() => { });
|
|
358
|
+
});
|
|
359
|
+
expect(apiSearchMock).toHaveBeenCalledWith(request);
|
|
360
|
+
expect(hook.result.current.response).toEqual(initialResponse);
|
|
361
|
+
});
|
|
362
|
+
it('should throw an error if the request throws an error', async () => {
|
|
363
|
+
const hook = renderProvider();
|
|
364
|
+
const request = {
|
|
365
|
+
query: 'test',
|
|
366
|
+
rows: TEST_PAGE_SIZE,
|
|
367
|
+
offset: 0
|
|
368
|
+
};
|
|
369
|
+
apiSearchMock.mockRejectedValue(new Error('Request failed'));
|
|
370
|
+
await expect(act(async () => {
|
|
371
|
+
await hook.result.current.request(apiSearchMock, request);
|
|
372
|
+
})).rejects.toThrow('Request failed');
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
describe('getSearchRequestData', () => {
|
|
376
|
+
it.for([
|
|
377
|
+
{ description: 'no remove count', removeCount: 0 },
|
|
378
|
+
{ description: 'with remove count', removeCount: 5 }
|
|
379
|
+
])('should modify offset if it is provided - $description', ({ removeCount }) => {
|
|
380
|
+
const hook = renderProvider({
|
|
381
|
+
items: [],
|
|
382
|
+
offset: 0,
|
|
383
|
+
rows: TEST_PAGE_SIZE,
|
|
384
|
+
total: TEST_TOTAL_COUNT,
|
|
385
|
+
removeCount: removeCount
|
|
386
|
+
});
|
|
387
|
+
const modifiedRequest = hook.result.current.getSearchRequestData({ offset: TEST_PAGE_SIZE });
|
|
388
|
+
expect(modifiedRequest.offset).toBe(TEST_PAGE_SIZE - removeCount);
|
|
389
|
+
});
|
|
390
|
+
it('should not modify offset if it is not provided', () => {
|
|
391
|
+
const hook = renderProvider({
|
|
392
|
+
items: [],
|
|
393
|
+
offset: 0,
|
|
394
|
+
rows: TEST_PAGE_SIZE,
|
|
395
|
+
total: TEST_TOTAL_COUNT,
|
|
396
|
+
removeCount: 5
|
|
397
|
+
});
|
|
398
|
+
const modifiedRequest = hook.result.current.getSearchRequestData({ query: 'value' });
|
|
399
|
+
expect(modifiedRequest.offset).not.toBeDefined();
|
|
400
|
+
});
|
|
401
|
+
it('should not modify offset if response is uninitialised', () => {
|
|
402
|
+
const hook = renderProvider();
|
|
403
|
+
const modifiedRequest = hook.result.current.getSearchRequestData({ offset: TEST_PAGE_SIZE });
|
|
404
|
+
expect(modifiedRequest.offset).toBe(TEST_PAGE_SIZE);
|
|
405
|
+
});
|
|
406
|
+
});
|
|
@@ -151,7 +151,10 @@ describe('ViewContext', () => {
|
|
|
151
151
|
it('should allow users to edit views', async () => {
|
|
152
152
|
const result = await act(async () => hook.result.current('example_view_id', { query: DEFAULT_QUERY }));
|
|
153
153
|
expect(hput).toHaveBeenCalledOnce();
|
|
154
|
-
|
|
154
|
+
const [url, body, ...rest] = vi.mocked(hput).mock.calls[0];
|
|
155
|
+
expect(url).toBe('/api/v1/view/example_view_id');
|
|
156
|
+
expect(body).toEqual({ query: DEFAULT_QUERY });
|
|
157
|
+
expect(rest.every(v => v === undefined)).toBe(true);
|
|
155
158
|
expect(result).toEqual(MOCK_RESPONSES['/api/v1/view/example_view_id']);
|
|
156
159
|
});
|
|
157
160
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/// <reference types="vitest" />
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
|
+
import { setupReactRouterMock } from '@cccsaurora/howler-ui/tests/mocks';
|
|
5
|
+
import { vi } from 'vitest';
|
|
6
|
+
setupReactRouterMock();
|
|
7
|
+
import CustomButton from './CustomButton';
|
|
8
|
+
describe('CustomButton', () => {
|
|
9
|
+
afterAll(() => vi.resetModules());
|
|
10
|
+
describe('rendering', () => {
|
|
11
|
+
it('renders a button element', () => {
|
|
12
|
+
render(_jsx(CustomButton, { children: "Click me" }));
|
|
13
|
+
expect(screen.getByRole('button', { name: 'Click me' })).toBeInTheDocument();
|
|
14
|
+
});
|
|
15
|
+
it('renders with MUI Button classes', () => {
|
|
16
|
+
render(_jsx(CustomButton, { children: "Test" }));
|
|
17
|
+
expect(screen.getByRole('button')).toHaveClass('MuiButton-root');
|
|
18
|
+
});
|
|
19
|
+
it('renders children text', () => {
|
|
20
|
+
render(_jsx(CustomButton, { children: "My Button" }));
|
|
21
|
+
expect(screen.getByText('My Button')).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
describe('variants', () => {
|
|
25
|
+
it('renders contained variant', () => {
|
|
26
|
+
render(_jsx(CustomButton, { variant: "contained", children: "Contained" }));
|
|
27
|
+
expect(screen.getByRole('button')).toHaveClass('MuiButton-contained');
|
|
28
|
+
});
|
|
29
|
+
it('renders outlined variant', () => {
|
|
30
|
+
render(_jsx(CustomButton, { variant: "outlined", children: "Outlined" }));
|
|
31
|
+
expect(screen.getByRole('button')).toHaveClass('MuiButton-outlined');
|
|
32
|
+
});
|
|
33
|
+
it('renders text variant', () => {
|
|
34
|
+
render(_jsx(CustomButton, { variant: "text", children: "Text" }));
|
|
35
|
+
expect(screen.getByRole('button')).toHaveClass('MuiButton-text');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe('MUI color prop', () => {
|
|
39
|
+
it('renders with primary color by default', () => {
|
|
40
|
+
render(_jsx(CustomButton, { children: "Primary" }));
|
|
41
|
+
expect(screen.getByRole('button')).toHaveClass('MuiButton-colorPrimary');
|
|
42
|
+
});
|
|
43
|
+
it('renders with secondary color', () => {
|
|
44
|
+
render(_jsx(CustomButton, { color: "secondary", children: "Secondary" }));
|
|
45
|
+
expect(screen.getByRole('button')).toHaveClass('MuiButton-colorSecondary');
|
|
46
|
+
});
|
|
47
|
+
it('renders with error color', () => {
|
|
48
|
+
render(_jsx(CustomButton, { color: "error", children: "Error" }));
|
|
49
|
+
expect(screen.getByRole('button')).toHaveClass('MuiButton-colorError');
|
|
50
|
+
});
|
|
51
|
+
it('renders with inherit color for custom hex colors', () => {
|
|
52
|
+
render(_jsx(CustomButton, { color: "#ff0000", children: "Custom" }));
|
|
53
|
+
expect(screen.getByRole('button')).toHaveClass('MuiButton-colorInherit');
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe('progress', () => {
|
|
57
|
+
it('shows a progress indicator when progress is true', () => {
|
|
58
|
+
render(_jsx(CustomButton, { progress: true, children: "Loading" }));
|
|
59
|
+
expect(screen.getByRole('progressbar')).toBeInTheDocument();
|
|
60
|
+
});
|
|
61
|
+
it('does not show a progress indicator when progress is false', () => {
|
|
62
|
+
render(_jsx(CustomButton, { progress: false, children: "Not Loading" }));
|
|
63
|
+
expect(screen.queryByRole('progressbar')).not.toBeInTheDocument();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe('tooltip', () => {
|
|
67
|
+
it('wraps button in a tooltip when tooltip prop is provided', () => {
|
|
68
|
+
render(_jsx(CustomButton, { tooltip: "Help text", children: "Hover me" }));
|
|
69
|
+
// Tooltip wraps in a span
|
|
70
|
+
const button = screen.getByRole('button');
|
|
71
|
+
expect(button.closest('span')).toBeInTheDocument();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe('routing', () => {
|
|
75
|
+
it('wraps button in a Link when route prop is provided', () => {
|
|
76
|
+
render(_jsx(CustomButton, { route: "/test-route", children: "Navigate" }));
|
|
77
|
+
const link = screen.getByRole('link');
|
|
78
|
+
expect(link).toHaveAttribute('href', '/test-route');
|
|
79
|
+
});
|
|
80
|
+
it('wraps button in an anchor tag when href prop is provided', () => {
|
|
81
|
+
render(_jsx(CustomButton, { href: "https://example.com", children: "External" }));
|
|
82
|
+
const link = screen.getByRole('link');
|
|
83
|
+
expect(link).toHaveAttribute('href', 'https://example.com');
|
|
84
|
+
});
|
|
85
|
+
it('does not wrap button in link when neither route nor href is provided', () => {
|
|
86
|
+
render(_jsx(CustomButton, { children: "Plain" }));
|
|
87
|
+
expect(screen.queryByRole('link')).not.toBeInTheDocument();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
describe('disabled state', () => {
|
|
91
|
+
it('renders disabled button when disabled prop is true', () => {
|
|
92
|
+
render(_jsx(CustomButton, { disabled: true, children: "Disabled" }));
|
|
93
|
+
expect(screen.getByRole('button')).toBeDisabled();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe('sizes', () => {
|
|
97
|
+
it('renders small size', () => {
|
|
98
|
+
render(_jsx(CustomButton, { size: "small", children: "Small" }));
|
|
99
|
+
expect(screen.getByRole('button')).toHaveClass('MuiButton-sizeSmall');
|
|
100
|
+
});
|
|
101
|
+
it('renders large size', () => {
|
|
102
|
+
render(_jsx(CustomButton, { size: "large", children: "Large" }));
|
|
103
|
+
expect(screen.getByRole('button')).toHaveClass('MuiButton-sizeLarge');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/// <reference types="vitest" />
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
|
+
import FlexOne from './FlexOne';
|
|
5
|
+
describe('FlexOne', () => {
|
|
6
|
+
describe('rendering', () => {
|
|
7
|
+
it('renders a div element', () => {
|
|
8
|
+
const { container } = render(_jsx(FlexOne, {}));
|
|
9
|
+
expect(container.querySelector('div')).toBeInTheDocument();
|
|
10
|
+
});
|
|
11
|
+
it('applies flex: 1 style', () => {
|
|
12
|
+
const { container } = render(_jsx(FlexOne, {}));
|
|
13
|
+
expect(container.firstChild).toHaveStyle({ flex: '1' });
|
|
14
|
+
});
|
|
15
|
+
it('renders children inside the div', () => {
|
|
16
|
+
render(_jsx(FlexOne, { children: _jsx("span", { id: "inner", children: "hello" }) }));
|
|
17
|
+
expect(screen.getByTestId('inner')).toBeInTheDocument();
|
|
18
|
+
});
|
|
19
|
+
it('renders without children without error', () => {
|
|
20
|
+
const { container } = render(_jsx(FlexOne, {}));
|
|
21
|
+
expect(container.firstChild).toBeInTheDocument();
|
|
22
|
+
expect(container.firstChild).toBeEmptyDOMElement();
|
|
23
|
+
});
|
|
24
|
+
it('renders multiple children', () => {
|
|
25
|
+
render(_jsxs(FlexOne, { children: [_jsx("span", { id: "a", children: "A" }), _jsx("span", { id: "b", children: "B" })] }));
|
|
26
|
+
expect(screen.getByTestId('a')).toBeInTheDocument();
|
|
27
|
+
expect(screen.getByTestId('b')).toBeInTheDocument();
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -30,6 +30,10 @@ const TuiListBaseRoot = styled('div')(({ theme }) => ({
|
|
|
30
30
|
display: 'flex',
|
|
31
31
|
backgroundColor: emphasize(theme.palette.background.default, 0.04)
|
|
32
32
|
}
|
|
33
|
+
},
|
|
34
|
+
'.elementDisabled': {
|
|
35
|
+
color: theme.palette.text.disabled,
|
|
36
|
+
cursor: 'default'
|
|
33
37
|
}
|
|
34
38
|
}));
|
|
35
39
|
const TuiListBase = ({ keyboard = false, onSelect, children }) => {
|