Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UILD-405: change state management library #52

Merged
merged 46 commits into from
Dec 14, 2024
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
3d71875
add basic store and example
SKarolFolio Nov 28, 2024
073fb44
add basic selectors
SKarolFolio Nov 28, 2024
f821afa
update status store; add marc store
SKarolFolio Nov 28, 2024
b729226
Merge branch 'master' of https://github.com/folio-org/ui-linked-data …
SKarolFolio Nov 28, 2024
bc5b744
marc preview state update
SKarolFolio Nov 28, 2024
f4ba272
refactor selectors usage
SKarolFolio Nov 29, 2024
bf45ca1
fix styles for search title
SKarolFolio Nov 29, 2024
4b8c1cc
update slices creator
SKarolFolio Nov 29, 2024
3b67282
fixed usage of the store methods
SKarolFolio Nov 29, 2024
f09ed72
selectors refactoring
SKarolFolio Nov 29, 2024
51fce8f
basic unit tests
SKarolFolio Nov 29, 2024
92eed98
code refactoring and basic unit tests
SKarolFolio Nov 29, 2024
8615609
code refactoring
SKarolFolio Nov 29, 2024
88577c6
tests refactoring
SKarolFolio Nov 29, 2024
f8a241c
code refactoring: rename properties and methods; disable devtools in …
SKarolFolio Dec 2, 2024
47f28b0
refactoring: rename a method for adding a single item
SKarolFolio Dec 2, 2024
f3bd745
state slice - add unit tests
SKarolFolio Dec 2, 2024
0d4ff46
tests refactoring
SKarolFolio Dec 2, 2024
2152a97
remove unused Recoil code
SKarolFolio Dec 2, 2024
6afd2d6
migrate loading state
SKarolFolio Dec 2, 2024
2c88a04
fix devtools usage
SKarolFolio Dec 2, 2024
62e7550
refactor store generation
SKarolFolio Dec 2, 2024
922289f
migrate profiles and schema data
SKarolFolio Dec 3, 2024
a8fd004
migrate user values
SKarolFolio Dec 4, 2024
9e58809
refactor unit tests to apply multiple stores
SKarolFolio Dec 4, 2024
9eaf316
migrate user input
SKarolFolio Dec 4, 2024
4d11ced
store types refactoring
SKarolFolio Dec 5, 2024
a3fd6f7
migrate config
SKarolFolio Dec 5, 2024
025aa70
migrate UI controls store
SKarolFolio Dec 5, 2024
02d7928
setters usage refactoring
SKarolFolio Dec 5, 2024
1baf4bc
migrate search store
SKarolFolio Dec 6, 2024
364475e
delete unused recoil code
SKarolFolio Dec 10, 2024
bd5ceaa
tests minor fix
SKarolFolio Dec 10, 2024
562a99e
Merge branch 'master' of https://github.com/folio-org/ui-linked-data …
SKarolFolio Dec 10, 2024
2771cf7
unit tests fixes
SKarolFolio Dec 10, 2024
bb71499
change store structure
SKarolFolio Dec 10, 2024
501d6d7
debounce user input
SKarolFolio Dec 10, 2024
0c2aec4
store generation refactored
SKarolFolio Dec 11, 2024
7c27031
update selectors
SKarolFolio Dec 11, 2024
2804335
stores config refactoring
SKarolFolio Dec 11, 2024
94f4238
comments updated
SKarolFolio Dec 12, 2024
2ca2ab1
Merge branch 'master' of https://github.com/folio-org/ui-linked-data …
SKarolFolio Dec 12, 2024
6784f41
import path fixed
SKarolFolio Dec 12, 2024
56728a8
types fixed
SKarolFolio Dec 12, 2024
ea97aca
fixes after code review
SKarolFolio Dec 13, 2024
3c1ac31
Merge branch 'master' of https://github.com/folio-org/ui-linked-data …
SKarolFolio Dec 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 0 additions & 21 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
"react-intl": "^6.4.4",
"react-router-dom": "^6.20.0",
"react-select": "^5.8.0",
"recoil": "^0.7.7",
"uuid": "^9.0.0"
"uuid": "^9.0.0",
"zustand": "^5.0.1"
},
"devDependencies": {
"@testing-library/dom": "^10.4.0",
Expand Down
12 changes: 3 additions & 9 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { FC, Suspense, useEffect, useRef } from 'react';
import { Navigate, RouteObject, RouterProvider, createBrowserRouter } from 'react-router-dom';
import { RecoilRoot, useSetRecoilState } from 'recoil';
import { ErrorBoundary } from '@components/ErrorBoundary';
import { Loading } from '@components/Loading';
import { ROUTES } from '@common/constants/routes.constants';
import { DEFAULT_LOCALE } from '@common/constants/i18n.constants';
import { OKAPI_CONFIG } from '@common/constants/api.constants';
import { localStorageService } from '@common/services/storage';
import { Root, Search, EditWrapper, ExternalResourcePreview } from '@views';
import state from '@state';
import en from '../translations/ui-linked-data/en.json';
import { AsyncIntlProvider, ServicesProvider } from './providers';
import './App.scss';
import { useConfigState } from './store';

type IContainer = {
routePrefix?: string;
Expand Down Expand Up @@ -54,8 +53,7 @@ export const routes: RouteObject[] = [
const createRouter = (basename: string) => createBrowserRouter(routes, { basename });

const Container: FC<IContainer> = ({ routePrefix = '', config }) => {
const setCustomEvents = useSetRecoilState(state.config.customEvents);
const setHasNavigationOrigin = useSetRecoilState(state.config.hasNavigationOrigin);
const { setCustomEvents, setHasNavigationOrigin } = useConfigState();
const cachedMessages = useRef({ [DEFAULT_LOCALE]: en });

useEffect(() => {
Expand All @@ -82,9 +80,5 @@ export const App: FC<IContainer> = ({ routePrefix = '', config }) => {
config && localStorageService.serialize(OKAPI_CONFIG, config);
}, [config]);

return (
<RecoilRoot>
<Container routePrefix={routePrefix} config={config} />
</RecoilRoot>
);
return <Container routePrefix={routePrefix} config={config} />;
};
1 change: 1 addition & 0 deletions src/common/constants/bundle.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const IS_PROD_MODE = import.meta.env.PROD;
7 changes: 3 additions & 4 deletions src/common/hooks/useCommonStatus.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { useSetRecoilState } from 'recoil';
import { UserNotificationFactory } from '@common/services/userNotification';
import state from '@state';
import { useStatusState } from '@src/store';

export const useCommonStatus = () => {
const setCommonStatus = useSetRecoilState(state.status.commonMessages);
const { addStatusMessagesItem } = useStatusState();

return {
set: (l10nId: string, type: StatusType) => {
setCommonStatus(currentStatus => [...currentStatus, UserNotificationFactory.createMessage(type, l10nId)]);
addStatusMessagesItem?.(UserNotificationFactory.createMessage(type, l10nId));
},
};
};
20 changes: 11 additions & 9 deletions src/common/hooks/useComplexLookup.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ChangeEvent, useCallback, useState } from 'react';
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil';
import {
generateEmptyValueUuid,
getLinkedField,
Expand All @@ -8,10 +7,10 @@ import {
} from '@common/helpers/complexLookup.helper';
import { __MOCK_URI_CHANGE_WHEN_IMPLEMENTING } from '@common/constants/complexLookup.constants';
import { AdvancedFieldType } from '@common/constants/uiControls.constants';
import state from '@state';
import { useModalControls } from './useModalControls';
import { useMarcData } from './useMarcData';
import { useServicesContext } from './useServicesContext';
import { useInputsState, useMarcPreviewState, useProfileState, useUIState } from '@src/store';

export const useComplexLookup = ({
entry,
Expand All @@ -26,14 +25,17 @@ export const useComplexLookup = ({
}) => {
const { selectedEntriesService } = useServicesContext() as Required<ServicesParams>;
const [localValue, setLocalValue] = useState<UserValueContents[]>(value || []);
const schema = useRecoilValue(state.config.schema);
const marcPreviewMetadata = useRecoilValue(state.data.marcPreviewMetadata);
const [selectedEntries, setSelectedEntries] = useRecoilState(state.config.selectedEntries);
const resetMarcPreviewData = useResetRecoilState(state.data.marcPreviewData);
const resetMarcPreviewMetadata = useResetRecoilState(state.data.marcPreviewMetadata);
const resetIsMarcPreviewOpen = useResetRecoilState(state.ui.isMarcPreviewOpen);
const { schema } = useProfileState();
const { selectedEntries, setSelectedEntries } = useInputsState();
const {
setComplexValue,
resetComplexValue: resetMarcPreviewData,
metaData: marcPreviewMetadata,
SKarolFolio marked this conversation as resolved.
Show resolved Hide resolved
resetMetaData: resetMarcPreviewMetadata,
} = useMarcPreviewState();
const { resetIsMarcPreviewOpen } = useUIState();
const { isModalOpen, setIsModalOpen, openModal } = useModalControls();
const { fetchMarcData } = useMarcData(state.data.marcPreviewData);
const { fetchMarcData } = useMarcData(setComplexValue);
const { uuid, linkedEntry } = entry;
const linkedField = getLinkedField({ schema, linkedEntry });

Expand Down
6 changes: 2 additions & 4 deletions src/common/hooks/useComplexLookupSearchResults.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import { useCallback, useMemo } from 'react';
import { useRecoilValue } from 'recoil';
import { useIntl } from 'react-intl';
import { type Row } from '@components/Table';
import { useSearchContext } from '@common/hooks/useSearchContext';
import { ComplexLookupSearchResultsProps } from '@components/ComplexLookupField/ComplexLookupSearchResults';
import state from '@state';
import { useSearchState } from '@src/store';

export const useComplexLookupSearchResults = ({
onTitleClick,
tableConfig,
searchResultsFormatter,
}: ComplexLookupSearchResultsProps) => {
const { onAssignRecord } = useSearchContext();
const data = useRecoilValue(state.search.data);
const sourceData = useRecoilValue(state.search.sourceData);
const { data, sourceData } = useSearchState();
const { formatMessage } = useIntl();

const applyActionItems = useCallback(
Expand Down
27 changes: 14 additions & 13 deletions src/common/hooks/useConfig.hook.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { useRef } from 'react';
import { useRecoilState, useSetRecoilState } from 'recoil';
import { v4 as uuidv4 } from 'uuid';
import state from '@state';
import { fetchProfiles } from '@common/api/profiles.api';
import { PROFILE_NAMES } from '@common/constants/bibframe.constants';
import { getPrimaryEntitiesFromRecord, getRecordTitle } from '@common/helpers/record.helper';
import { useInputsState, useProfileState } from '@src/store';
import { useProcessedRecordAndSchema } from './useProcessedRecordAndSchema.hook';
import { useServicesContext } from './useServicesContext';

Expand All @@ -31,15 +30,17 @@ type IBuildSchema = {
export const useConfig = () => {
const { schemaCreatorService, userValuesService, selectedEntriesService } =
useServicesContext() as Required<ServicesParams>;
const [profiles, setProfiles] = useRecoilState(state.config.profiles);
const setSelectedProfile = useSetRecoilState(state.config.selectedProfile);
const setUserValues = useSetRecoilState(state.inputs.userValues);
const [preparedFields, setPreparedFields] = useRecoilState(state.config.preparedFields);
const setSchema = useSetRecoilState(state.config.schema);
const setInitialSchemaKey = useSetRecoilState(state.config.initialSchemaKey);
const setSelectedEntries = useSetRecoilState(state.config.selectedEntries);
const setPreviewContent = useSetRecoilState(state.inputs.previewContent);
const setSelectedRecordBlocks = useSetRecoilState(state.inputs.selectedRecordBlocks);
const {
profiles,
setProfiles,
setSelectedProfile,
preparedFields,
setPreparedFields,
setInitialSchemaKey,
setSchema,
} = useProfileState();
const { setUserValues, previewContent, setPreviewContent, setSelectedRecordBlocks, setSelectedEntries } =
useInputsState();
const { getProcessedRecordAndSchema } = useProcessedRecordAndSchema();
const isProcessingProfiles = useRef(false);

Expand Down Expand Up @@ -125,8 +126,8 @@ export const useConfig = () => {
});

if (previewParams && recordId) {
setPreviewContent(prev => [
...(previewParams.singular ? [] : prev.filter(({ id }) => id !== recordId)),
setPreviewContent([
...(previewParams.singular ? [] : previewContent.filter(({ id }) => id !== recordId)),
{
id: recordId,
base: updatedSchema,
Expand Down
14 changes: 7 additions & 7 deletions src/common/hooks/useContainerEvents.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import state from '@state';
import { useRecoilValue } from 'recoil';
import { IS_EMBEDDED_MODE } from '@common/constants/build.constants';
import { dispatchEventWrapper, getWrapperAsWebComponent } from '@common/helpers/dom.helper';
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { IS_EMBEDDED_MODE } from '@common/constants/build.constants';
import { dispatchEventWrapper, getWrapperAsWebComponent } from '@common/helpers/dom.helper';
import { ROUTES } from '@common/constants/routes.constants';
import { useConfigState, useStatusState } from '@src/store';

type IUseContainerEvents =
| {
Expand All @@ -14,16 +13,17 @@ type IUseContainerEvents =
| undefined;

export const useContainerEvents = ({ onTriggerModal, watchEditedState = false }: IUseContainerEvents = {}) => {
const hasNavigationOrigin = useRecoilValue(state.config.hasNavigationOrigin);
const isEdited = useRecoilValue(state.status.recordIsEdited);
const { hasNavigationOrigin } = useConfigState();
const { isEditedRecord: isEdited } = useStatusState();
const { customEvents } = useConfigState();
const {
BLOCK_NAVIGATION,
UNBLOCK_NAVIGATION,
TRIGGER_MODAL,
PROCEED_NAVIGATION,
NAVIGATE_TO_ORIGIN,
DROP_NAVIGATE_TO_ORIGIN,
} = useRecoilValue(state.config.customEvents) ?? {};
} = customEvents ?? {};
const navigate = useNavigate();

useEffect(() => {
Expand Down
24 changes: 6 additions & 18 deletions src/common/hooks/useFetchSearchData.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { useCallback } from 'react';
import { useSetRecoilState, useRecoilState, SetterOrUpdater, useResetRecoilState } from 'recoil';
import { getByIdentifier } from '@common/api/search.api';
import { SearchIdentifiers, SearchSegment } from '@common/constants/search.constants';
import { SearchableIndexQuerySelector } from '@common/constants/complexLookup.constants';
import { StatusType } from '@common/constants/status.constants';
import { normalizeQuery } from '@common/helpers/search.helper';
import { normalizeLccn } from '@common/helpers/validations.helper';
import { UserNotificationFactory } from '@common/services/userNotification';
import state from '@state';
import { useLoadingState, useSearchState, useStatusState } from '@src/store';
import { useSearchContext } from './useSearchContext';

export const useFetchSearchData = () => {
Expand All @@ -24,13 +23,9 @@ export const useFetchSearchData = () => {
buildSearchQuery,
precedingRecordsCount,
} = useSearchContext();
const setIsLoading = useSetRecoilState(state.loadingState.isLoading);
const setMessage = useSetRecoilState(state.search.message);
const [data, setData] = useRecoilState(state.search.data);
const resetData = useResetRecoilState(state.search.data);
const setPageMetadata = useSetRecoilState(state.search.pageMetadata);
const setStatusMessages = useSetRecoilState(state.status.commonMessages);
const resetStatusMessage = useResetRecoilState(state.status.commonMessages);
const { setIsLoading } = useLoadingState();
const { setMessage, data, setData, resetData, setPageMetadata } = useSearchState();
const { addStatusMessagesItem, resetStatusMessages } = useStatusState();

const validateAndNormalizeQuery = useCallback(
(type: SearchIdentifiers, query: string) => {
Expand Down Expand Up @@ -130,13 +125,6 @@ export const useFetchSearchData = () => {
});
};

const handleFetchError = (setStatusMessages: SetterOrUpdater<StatusEntry[]>) => {
setStatusMessages(currentStatus => [
...currentStatus,
UserNotificationFactory.createMessage(StatusType.error, 'ld.errorFetching'),
]);
};

const fetchData = useCallback(
async ({
query,
Expand All @@ -145,7 +133,7 @@ export const useFetchSearchData = () => {
selectedSegment,
baseQuerySelector = SearchableIndexQuerySelector.Query,
}: FetchDataParams) => {
resetStatusMessage();
resetStatusMessages();
const selectedNavigationSegment = selectedSegment ?? navigationSegment?.value;

data && resetData();
Expand Down Expand Up @@ -189,7 +177,7 @@ export const useFetchSearchData = () => {
setData(content);
setPageMetadata({ totalPages, totalElements: totalRecords, prev, next });
} catch {
handleFetchError(setStatusMessages);
addStatusMessagesItem?.(UserNotificationFactory.createMessage(StatusType.error, 'ld.errorFetching'));
} finally {
setIsLoading(false);
}
Expand Down
11 changes: 3 additions & 8 deletions src/common/hooks/useLoadSearchResults.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import { useEffect, useRef } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil';
import { SearchQueryParams } from '@common/constants/routes.constants';
import { SEARCH_RESULTS_LIMIT, SearchIdentifiers } from '@common/constants/search.constants';
import { useLoadingState, useSearchState } from '@src/store';
import { useSearchContext } from './useSearchContext';
import state from '@state';

export const useLoadSearchResults = (
fetchData: ({ query, searchBy, offset, selectedSegment, baseQuerySelector }: FetchDataParams) => Promise<void>,
) => {
const { hasSearchParams, defaultSearchBy, defaultQuery, getSearchSourceData, getSearchFacetsData } =
useSearchContext();
const setData = useSetRecoilState(state.search.data);
const setSearchBy = useSetRecoilState(state.search.index);
const setIsLoading = useSetRecoilState(state.loadingState.isLoading);
const setQuery = useSetRecoilState(state.search.query);
const [forceRefresh, setForceRefresh] = useRecoilState(state.search.forceRefresh);
const resetFacetsData = useResetRecoilState(state.search.facetsData);
const { setIsLoading } = useLoadingState();
const { setQuery, setData, setSearchBy, forceRefresh, setForceRefresh, resetFacetsData } = useSearchState();
const [searchParams] = useSearchParams();
const queryParam = searchParams.get(SearchQueryParams.Query);
const searchByParam = searchParams.get(SearchQueryParams.SearchBy);
Expand Down
18 changes: 6 additions & 12 deletions src/common/hooks/useMarcData.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { RecoilState, useResetRecoilState, useSetRecoilState } from 'recoil';
import { getMarcRecord } from '@common/api/records.api';
import { StatusType } from '@common/constants/status.constants';
import { UserNotificationFactory } from '@common/services/userNotification';
import state from '@state';
import { useLoadingState, useStatusState } from '@src/store';

export const useMarcData = <T>(marcState: RecoilState<T>) => {
const setMarcPreviewData = useSetRecoilState(marcState);
const clearMarcData = useResetRecoilState(marcState);
const setIsLoading = useSetRecoilState(state.loadingState.isLoading);
const setStatus = useSetRecoilState(state.status.commonMessages);
export const useMarcData = (setMarcPreviewData: (value: any) => void) => {
const { setIsLoading } = useLoadingState();
SKarolFolio marked this conversation as resolved.
Show resolved Hide resolved
const { addStatusMessagesItem } = useStatusState();

const fetchMarcData = async (recordId?: string, endpointUrl?: string): Promise<MarcDTO | undefined> => {
if (!recordId) return undefined;
Expand All @@ -22,16 +19,13 @@ export const useMarcData = <T>(marcState: RecoilState<T>) => {

setMarcPreviewData(marcData);
} catch (error) {
setStatus(currentStatus => [
...currentStatus,
UserNotificationFactory.createMessage(StatusType.error, 'ld.cantLoadMarc'),
]);
addStatusMessagesItem?.(UserNotificationFactory.createMessage(StatusType.error, 'ld.cantLoadMarc'));
} finally {
setIsLoading(false);
}

return marcData;
};

return { fetchMarcData, clearMarcData };
return { fetchMarcData };
};
5 changes: 2 additions & 3 deletions src/common/hooks/useNavigateToEditPage.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { ROUTES, QueryParams } from '@common/constants/routes.constants';
import state from '@state';
import { useSearchState } from '@src/store';
import { useNavigate } from 'react-router-dom';
import { useRecoilValue } from 'recoil';

export const useNavigateToEditPage = () => {
const navigate = useNavigate();
const navigationState = useRecoilValue(state.search.navigationState);
const { navigationState } = useSearchState();

const navigateAsDuplicate = (duplicateId: string) => {
navigate(`${ROUTES.RESOURCE_CREATE.uri}?${QueryParams.CloneOf}=${duplicateId}`, { state: navigationState });
Expand Down
Loading
Loading