Rework relations (#3431)

* Rework relations

* Fix tests
This commit is contained in:
Charles Bochet
2024-01-15 12:07:23 +01:00
committed by GitHub
parent 8c96acc2a3
commit 16a24c5f0c
60 changed files with 392 additions and 463 deletions

View File

@ -1,9 +1,10 @@
import { useParams } from 'react-router-dom';
import { useRecoilState } from 'recoil';
import { useSetRecoilState } from 'recoil';
import { useFavorites } from '@/favorites/hooks/useFavorites';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation';
import { parseFieldRelationType } from '@/object-metadata/utils/parseFieldRelationType';
import { parseFieldType } from '@/object-metadata/utils/parseFieldType';
import {
@ -62,7 +63,7 @@ export const RecordShowPage = () => {
const { favorites, createFavorite, deleteFavorite } = useFavorites();
const [, setEntityFields] = useRecoilState(
const setEntityFields = useSetRecoilState(
entityFieldsFamilyState(objectRecordId ?? ''),
);
@ -274,8 +275,21 @@ export const RecordShowPage = () => {
)}
</PropertyBox>
{isRelationFieldCardEnabled &&
relationFieldMetadataItems.map(
(fieldMetadataItem, index) => (
relationFieldMetadataItems
.filter((item) => {
const relationObjectMetadataItem =
item.toRelationMetadata
? item.toRelationMetadata.fromObjectMetadata
: item.fromRelationMetadata?.toObjectMetadata;
if (!relationObjectMetadataItem) {
return false;
}
return isObjectMetadataAvailableForRelation(
relationObjectMetadataItem,
);
})
.map((fieldMetadataItem, index) => (
<FieldContext.Provider
key={record.id + fieldMetadataItem.id}
value={{
@ -294,8 +308,7 @@ export const RecordShowPage = () => {
>
<RecordRelationFieldCardSection />
</FieldContext.Provider>
),
)}
))}
</>
)}
</ShowPageLeftContainer>

View File

@ -6,7 +6,9 @@ import { useRelationField } from '../../hooks/useRelationField';
export const RelationFieldDisplay = () => {
const { fieldValue, fieldDefinition } = useRelationField();
const { identifiersMapper } = useRelationPicker();
const { identifiersMapper } = useRelationPicker({
relationPickerScopeId: 'relation-picker',
});
if (!fieldValue || !fieldDefinition || !identifiersMapper) {
return <></>;

View File

@ -19,7 +19,9 @@ export const useChipField = () => {
const record = useRecoilValue<any | null>(entityFieldsFamilyState(entityId));
const { identifiersMapper } = useRelationPicker();
const { identifiersMapper } = useRelationPicker({
relationPickerScopeId: 'relation-picker',
});
return {
basePathToShowPage,

View File

@ -19,6 +19,7 @@ import { useUpsertRecordFromState } from '@/object-record/hooks/useUpsertRecordF
import { RecordRelationFieldCardContent } from '@/object-record/record-relation-card/components/RecordRelationFieldCardContent';
import { SingleEntitySelectMenuItemsWithSearch } from '@/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch';
import { useRelationPicker } from '@/object-record/relation-picker/hooks/useRelationPicker';
import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope';
import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect';
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
import { IconForbid, IconPlus } from '@/ui/display/icon';
@ -153,12 +154,10 @@ export const RecordRelationFieldCardSection = () => {
const { closeDropdown, isDropdownOpen } = useDropdown(dropdownId);
const {
identifiersMapper,
relationPickerSearchFilter,
searchQuery,
setRelationPickerSearchFilter,
} = useRelationPicker();
const { relationPickerSearchFilter, setRelationPickerSearchFilter } =
useRelationPicker({ relationPickerScopeId: dropdownId });
const { identifiersMapper, searchQuery } = useRelationPicker();
const entities = useFilteredSearchEntityQuery({
filters: [
@ -225,53 +224,55 @@ export const RecordRelationFieldCardSection = () => {
return (
<Section>
<StyledHeader isDropdownOpen={isDropdownOpen}>
<StyledTitle>
<StyledTitleLabel>{fieldDefinition.label}</StyledTitleLabel>
{parseFieldRelationType(relationFieldMetadataItem) ===
'TO_ONE_OBJECT' && (
<StyledLink to={filterLinkHref}>
All ({relationRecords.length})
</StyledLink>
)}
</StyledTitle>
<DropdownScope dropdownScopeId={dropdownId}>
<StyledAddDropdown
dropdownId={dropdownId}
dropdownPlacement="right-start"
onClose={handleCloseRelationPickerDropdown}
clickableComponent={
<LightIconButton
className="displayOnHover"
Icon={IconPlus}
accent="tertiary"
/>
}
dropdownComponents={
<SingleEntitySelectMenuItemsWithSearch
EmptyIcon={IconForbid}
entitiesToSelect={entities.entitiesToSelect}
loading={entities.loading}
onEntitySelected={handleRelationPickerEntitySelected}
/>
}
dropdownHotkeyScope={{
scope: dropdownId,
}}
/>
</DropdownScope>
</StyledHeader>
{!!relationRecords.length && (
<Card>
{relationRecords.slice(0, 5).map((relationRecord, index) => (
<RecordRelationFieldCardContent
key={`${relationRecord.id}${relationLabelIdentifierFieldMetadata?.id}`}
divider={index < relationRecords.length - 1}
relationRecord={relationRecord}
<RelationPickerScope relationPickerScopeId={dropdownId}>
<StyledHeader isDropdownOpen={isDropdownOpen}>
<StyledTitle>
<StyledTitleLabel>{fieldDefinition.label}</StyledTitleLabel>
{parseFieldRelationType(relationFieldMetadataItem) ===
'TO_ONE_OBJECT' && (
<StyledLink to={filterLinkHref}>
All ({relationRecords.length})
</StyledLink>
)}
</StyledTitle>
<DropdownScope dropdownScopeId={dropdownId}>
<StyledAddDropdown
dropdownId={dropdownId}
dropdownPlacement="right-start"
onClose={handleCloseRelationPickerDropdown}
clickableComponent={
<LightIconButton
className="displayOnHover"
Icon={IconPlus}
accent="tertiary"
/>
}
dropdownComponents={
<SingleEntitySelectMenuItemsWithSearch
EmptyIcon={IconForbid}
entitiesToSelect={entities.entitiesToSelect}
loading={entities.loading}
onEntitySelected={handleRelationPickerEntitySelected}
/>
}
dropdownHotkeyScope={{
scope: dropdownId,
}}
/>
))}
</Card>
)}
</DropdownScope>
</StyledHeader>
{!!relationRecords.length && (
<Card>
{relationRecords.slice(0, 5).map((relationRecord, index) => (
<RecordRelationFieldCardContent
key={`${relationRecord.id}${relationLabelIdentifierFieldMetadata?.id}`}
divider={index < relationRecords.length - 1}
relationRecord={relationRecord}
/>
))}
</Card>
)}
</RelationPickerScope>
</Section>
);
};

View File

@ -4,6 +4,7 @@ import { expect, userEvent, within } from '@storybook/test';
import { IconUserCircle } from '@/ui/display/icon';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
import { RelationPickerDecorator } from '~/testing/decorators/RelationPickerDecorator';
import { mockedPeopleData } from '~/testing/mock-data/people';
import { sleep } from '~/testing/sleep';
@ -19,7 +20,11 @@ const entities = mockedPeopleData.map<EntityForSelect>((person) => ({
const meta: Meta<typeof SingleEntitySelect> = {
title: 'UI/Input/RelationPicker/SingleEntitySelect',
component: SingleEntitySelect,
decorators: [ComponentDecorator, ComponentWithRecoilScopeDecorator],
decorators: [
ComponentDecorator,
ComponentWithRecoilScopeDecorator,
RelationPickerDecorator,
],
argTypes: {
selectedEntity: {
options: entities.map(({ name }) => name),

View File

@ -7,9 +7,7 @@ export const useEntitySelectSearch = () => {
setRelationPickerPreselectedId,
relationPickerSearchFilter,
setRelationPickerSearchFilter,
} = useRelationPicker({
relationPickerScopeId: 'relation-picker',
});
} = useRelationPicker();
const debouncedSetSearchFilter = debounce(
setRelationPickerSearchFilter,