Add progressive loading to FIELD widget CARD display mode (#17202)
https://github.com/user-attachments/assets/3a444937-0290-4505-85e1-64b7b713bc3b - [x] Analyze the existing `FieldWidgetRelationCard` and `FieldWidgetMorphRelationCard` components - [x] Review existing patterns for "More" buttons (found `IconChevronDown` pattern) - [x] Create a reusable `FieldWidgetShowMoreButton` component for the "More (X)" button - [x] Update `FieldWidgetRelationCard` to show max 5 items initially with progressive loading - [x] Update `FieldWidgetMorphRelationCard` to show max 5 items initially with progressive loading - [x] Add Storybook story with play function to test progressive loading for regular relations - [x] Verify changes visually in Storybook (all tests pass) - [x] Run code review (no issues found) - [x] Run CodeQL security check (no code changes for CodeQL to analyze) - [x] Address PR review feedback: - [x] Move constants to separate files (`FieldWidgetRelationCardInitialVisibleItems.ts` and `FieldWidgetRelationCardLoadMoreIncrement.ts`) - [x] Add translation for "More (X)" string using `t` macro from `@lingui/core/macro` ## Summary This PR adds progressive loading functionality to the FIELD widget with CARD display mode: 1. **New Component**: Created `FieldWidgetShowMoreButton` - A styled button component that displays "More (X)" with a chevron icon, showing the remaining count of hidden items. 2. **Updated Components**: - `FieldWidgetRelationCard`: Now displays max 5 relation cards initially, with a "More (X)" button that loads 5 more items on each click - `FieldWidgetMorphRelationCard`: Same progressive loading behavior for morph relations 3. **Constants**: Moved to separate files following codebase conventions: - `FIELD_WIDGET_RELATION_CARD_INITIAL_VISIBLE_ITEMS` (5) - `FIELD_WIDGET_RELATION_CARD_LOAD_MORE_INCREMENT` (5) 4. **Storybook Story**: Added `OneToManyRelationCardWidgetWithProgressiveLoading` story with play function that tests: - Initial display of 5 items - "More (7)" button visibility - Loading additional items on click - Button disappearing when all items are shown <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>[RPL] Allow users to progressively load relations in FIELD widget with CARD display mode</issue_title> > <issue_description>## Current state > > The FIELD widget should display max 5 relations in CARD display mode. > > ## Goal > > Display a "More (12)" button. Clicking on this button loads 5 more items. After a click, "More (12)" becomes "More (7)". > > <img width="1334" height="712" alt="Image" src="https://github.com/user-attachments/assets/e801cca2-af99-4d87-8ce2-0c639775b0b1" /> > > ## Technical input > > `FieldWidgetRelationCard` already loads all the relations and maps over each one of them. Create a state that's updated when clicking on the More button, slicing the relations to display. > > ## Acceptance criteria > > - Must work for relations and morph relations in _card_ display mode > - Create stories to assert it works properly (with play function to actually test the components)</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes twentyhq/core-team-issues#2063 <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Devessier <29370468+Devessier@users.noreply.github.com> Co-authored-by: Baptiste Devessier <baptiste@devessier.fr>
This commit is contained in:
co-authored by
copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Devessier
Baptiste Devessier
parent
5a5e9eeb9e
commit
4ce6119be7
+30
-1
@@ -1,3 +1,4 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { Fragment, useState } from 'react';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
@@ -16,12 +17,19 @@ import {
|
||||
import { usePersistField } from '@/object-record/record-field/ui/hooks/usePersistField';
|
||||
import { type FieldDefinition } from '@/object-record/record-field/ui/types/FieldDefinition';
|
||||
import { type FieldMorphRelationMetadata } from '@/object-record/record-field/ui/types/FieldMetadata';
|
||||
import { FieldWidgetShowMoreButton } from '@/page-layout/widgets/field/components/FieldWidgetShowMoreButton';
|
||||
import { FIELD_WIDGET_RELATION_CARD_INITIAL_VISIBLE_ITEMS } from '@/page-layout/widgets/field/constants/FieldWidgetRelationCardInitialVisibleItems';
|
||||
import { FIELD_WIDGET_RELATION_CARD_LOAD_MORE_INCREMENT } from '@/page-layout/widgets/field/constants/FieldWidgetRelationCardLoadMoreIncrement';
|
||||
import { generateFieldWidgetInstanceId } from '@/page-layout/widgets/field/utils/generateFieldWidgetInstanceId';
|
||||
import { useCurrentWidget } from '@/page-layout/widgets/hooks/useCurrentWidget';
|
||||
import { useTargetRecord } from '@/ui/layout/contexts/useTargetRecord';
|
||||
import { RightDrawerProvider } from '@/ui/layout/right-drawer/contexts/RightDrawerContext';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const StyledShowMoreButtonContainer = styled.div`
|
||||
padding-top: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
type FieldWidgetMorphRelationCardProps = {
|
||||
fieldDefinition: FieldDefinition<FieldMorphRelationMetadata>;
|
||||
recordId: string;
|
||||
@@ -36,6 +44,9 @@ export const FieldWidgetMorphRelationCard = ({
|
||||
const widget = useCurrentWidget();
|
||||
|
||||
const [expandedItem, setExpandedItem] = useState('');
|
||||
const [visibleItemsCount, setVisibleItemsCount] = useState(
|
||||
FIELD_WIDGET_RELATION_CARD_INITIAL_VISIBLE_ITEMS,
|
||||
);
|
||||
const targetRecord = useTargetRecord();
|
||||
|
||||
const instanceId = generateFieldWidgetInstanceId({
|
||||
@@ -48,6 +59,12 @@ export const FieldWidgetMorphRelationCard = ({
|
||||
const handleItemClick = (id: string) =>
|
||||
setExpandedItem(id === expandedItem ? '' : id);
|
||||
|
||||
const handleShowMore = () => {
|
||||
setVisibleItemsCount(
|
||||
(prevCount) => prevCount + FIELD_WIDGET_RELATION_CARD_LOAD_MORE_INCREMENT,
|
||||
);
|
||||
};
|
||||
|
||||
const fieldMetadata = fieldDefinition.metadata;
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
@@ -110,6 +127,10 @@ export const FieldWidgetMorphRelationCard = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const visibleRecords = validRecords.slice(0, visibleItemsCount);
|
||||
const remainingCount = validRecords.length - visibleItemsCount;
|
||||
const hasMoreRecords = remainingCount > 0;
|
||||
|
||||
return (
|
||||
<RightDrawerProvider value={{ isInRightDrawer }}>
|
||||
<RecordFieldsScopeContextProvider value={{ scopeInstanceId: instanceId }}>
|
||||
@@ -124,7 +145,7 @@ export const FieldWidgetMorphRelationCard = ({
|
||||
>
|
||||
<FieldInputEventContext.Provider value={{ onSubmit: handleSubmit }}>
|
||||
<RecordDetailRecordsListContainer>
|
||||
{validRecords.map((item) => (
|
||||
{visibleRecords.map((item) => (
|
||||
<Fragment key={`${item.value.id}-${item.fieldMetadataId}`}>
|
||||
<RecordDetailRelationRecordsListItemEffect
|
||||
relationRecordId={item.value.id}
|
||||
@@ -139,6 +160,14 @@ export const FieldWidgetMorphRelationCard = ({
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
{hasMoreRecords && (
|
||||
<StyledShowMoreButtonContainer>
|
||||
<FieldWidgetShowMoreButton
|
||||
remainingCount={remainingCount}
|
||||
onClick={handleShowMore}
|
||||
/>
|
||||
</StyledShowMoreButtonContainer>
|
||||
)}
|
||||
</RecordDetailRecordsListContainer>
|
||||
</FieldInputEventContext.Provider>
|
||||
</FieldContext.Provider>
|
||||
|
||||
+30
-1
@@ -1,3 +1,4 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { Fragment, useState } from 'react';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
@@ -14,12 +15,19 @@ import {
|
||||
import { usePersistField } from '@/object-record/record-field/ui/hooks/usePersistField';
|
||||
import { type FieldDefinition } from '@/object-record/record-field/ui/types/FieldDefinition';
|
||||
import { type FieldRelationMetadata } from '@/object-record/record-field/ui/types/FieldMetadata';
|
||||
import { FieldWidgetShowMoreButton } from '@/page-layout/widgets/field/components/FieldWidgetShowMoreButton';
|
||||
import { FIELD_WIDGET_RELATION_CARD_INITIAL_VISIBLE_ITEMS } from '@/page-layout/widgets/field/constants/FieldWidgetRelationCardInitialVisibleItems';
|
||||
import { FIELD_WIDGET_RELATION_CARD_LOAD_MORE_INCREMENT } from '@/page-layout/widgets/field/constants/FieldWidgetRelationCardLoadMoreIncrement';
|
||||
import { generateFieldWidgetInstanceId } from '@/page-layout/widgets/field/utils/generateFieldWidgetInstanceId';
|
||||
import { useCurrentWidget } from '@/page-layout/widgets/hooks/useCurrentWidget';
|
||||
import { useTargetRecord } from '@/ui/layout/contexts/useTargetRecord';
|
||||
import { RightDrawerProvider } from '@/ui/layout/right-drawer/contexts/RightDrawerContext';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const StyledShowMoreButtonContainer = styled.div`
|
||||
padding-top: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
type FieldWidgetRelationCardProps = {
|
||||
fieldDefinition: FieldDefinition<FieldRelationMetadata>;
|
||||
relationValue: any;
|
||||
@@ -34,6 +42,9 @@ export const FieldWidgetRelationCard = ({
|
||||
const widget = useCurrentWidget();
|
||||
|
||||
const [expandedItem, setExpandedItem] = useState('');
|
||||
const [visibleItemsCount, setVisibleItemsCount] = useState(
|
||||
FIELD_WIDGET_RELATION_CARD_INITIAL_VISIBLE_ITEMS,
|
||||
);
|
||||
const targetRecord = useTargetRecord();
|
||||
|
||||
const instanceId = generateFieldWidgetInstanceId({
|
||||
@@ -46,6 +57,12 @@ export const FieldWidgetRelationCard = ({
|
||||
const handleItemClick = (recordId: string) =>
|
||||
setExpandedItem(recordId === expandedItem ? '' : recordId);
|
||||
|
||||
const handleShowMore = () => {
|
||||
setVisibleItemsCount(
|
||||
(prevCount) => prevCount + FIELD_WIDGET_RELATION_CARD_LOAD_MORE_INCREMENT,
|
||||
);
|
||||
};
|
||||
|
||||
const fieldMetadata = fieldDefinition.metadata;
|
||||
const relationObjectNameSingular =
|
||||
fieldMetadata.relationObjectMetadataNameSingular;
|
||||
@@ -107,6 +124,10 @@ export const FieldWidgetRelationCard = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const visibleRecords = records.slice(0, visibleItemsCount);
|
||||
const remainingCount = records.length - visibleItemsCount;
|
||||
const hasMoreRecords = remainingCount > 0;
|
||||
|
||||
return (
|
||||
<RightDrawerProvider value={{ isInRightDrawer }}>
|
||||
<RecordFieldsScopeContextProvider value={{ scopeInstanceId: instanceId }}>
|
||||
@@ -120,7 +141,7 @@ export const FieldWidgetRelationCard = ({
|
||||
}}
|
||||
>
|
||||
<FieldInputEventContext.Provider value={{ onSubmit: handleSubmit }}>
|
||||
{records.map((record) => (
|
||||
{visibleRecords.map((record) => (
|
||||
<Fragment key={record.id}>
|
||||
<RecordDetailRelationRecordsListItemEffect
|
||||
relationRecordId={record.id}
|
||||
@@ -139,6 +160,14 @@ export const FieldWidgetRelationCard = ({
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
{hasMoreRecords && (
|
||||
<StyledShowMoreButtonContainer>
|
||||
<FieldWidgetShowMoreButton
|
||||
remainingCount={remainingCount}
|
||||
onClick={handleShowMore}
|
||||
/>
|
||||
</StyledShowMoreButtonContainer>
|
||||
)}
|
||||
</FieldInputEventContext.Provider>
|
||||
</FieldContext.Provider>
|
||||
</RecordFieldsScopeContextProvider>
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
import { IconChevronDown } from 'twenty-ui/display';
|
||||
|
||||
type FieldWidgetShowMoreButtonProps = {
|
||||
remainingCount: number;
|
||||
onClick: () => void;
|
||||
};
|
||||
|
||||
const StyledButton = styled.button`
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
height: 24px;
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
font-family: ${({ theme }) => theme.font.family};
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
cursor: pointer;
|
||||
transition: color ${({ theme }) => theme.animation.duration.instant}s ease;
|
||||
|
||||
&:hover {
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledIcon = styled(IconChevronDown)`
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
`;
|
||||
|
||||
export const FieldWidgetShowMoreButton = ({
|
||||
remainingCount,
|
||||
onClick,
|
||||
}: FieldWidgetShowMoreButtonProps) => {
|
||||
return (
|
||||
<StyledButton data-testid="field-widget-show-more-button" onClick={onClick}>
|
||||
<StyledIcon />
|
||||
{t`More (${remainingCount})`}
|
||||
</StyledButton>
|
||||
);
|
||||
};
|
||||
+199
@@ -1867,3 +1867,202 @@ export const TimelineActivityRelationCardWidget: Story = {
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// Helper function to generate mock person records for progressive loading tests
|
||||
const generateMockPersonRecords = (count: number) => {
|
||||
const names = [
|
||||
{ firstName: 'Jane', lastName: 'Smith' },
|
||||
{ firstName: 'John', lastName: 'Williams' },
|
||||
{ firstName: 'Alice', lastName: 'Brown' },
|
||||
{ firstName: 'Bob', lastName: 'Davis' },
|
||||
{ firstName: 'Carol', lastName: 'Miller' },
|
||||
{ firstName: 'David', lastName: 'Wilson' },
|
||||
{ firstName: 'Emma', lastName: 'Moore' },
|
||||
{ firstName: 'Frank', lastName: 'Taylor' },
|
||||
{ firstName: 'Grace', lastName: 'Anderson' },
|
||||
{ firstName: 'Henry', lastName: 'Thomas' },
|
||||
{ firstName: 'Ivy', lastName: 'Jackson' },
|
||||
{ firstName: 'Jack', lastName: 'White' },
|
||||
];
|
||||
|
||||
return Array.from({ length: count }, (_, index) => {
|
||||
const nameInfo = names[index % names.length];
|
||||
const recordId = `person-${index + 1}`;
|
||||
return {
|
||||
id: recordId,
|
||||
__typename: 'Person',
|
||||
name: {
|
||||
__typename: 'FullName',
|
||||
firstName: nameInfo.firstName,
|
||||
lastName: `${nameInfo.lastName} ${index + 1}`,
|
||||
},
|
||||
emails: {
|
||||
__typename: 'Emails',
|
||||
primaryEmail: `${nameInfo.firstName.toLowerCase()}.${nameInfo.lastName.toLowerCase()}${index + 1}@example.com`,
|
||||
additionalEmails: [],
|
||||
},
|
||||
phones: {
|
||||
__typename: 'Phones',
|
||||
primaryPhoneNumber: `555000${(index + 1).toString().padStart(4, '0')}`,
|
||||
primaryPhoneCountryCode: '+1',
|
||||
primaryPhoneCallingCode: '+1',
|
||||
additionalPhones: [],
|
||||
},
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const OneToManyRelationCardWidgetWithProgressiveLoading: Story = {
|
||||
render: () => {
|
||||
const mockPeople = generateMockPersonRecords(12);
|
||||
const companyWithManyPeople = {
|
||||
...mockCompanyRecord,
|
||||
people: mockPeople.map(({ id, __typename, name }) => ({
|
||||
__typename,
|
||||
id,
|
||||
name,
|
||||
})),
|
||||
};
|
||||
|
||||
const widget: PageLayoutWidget = {
|
||||
__typename: 'PageLayoutWidget',
|
||||
id: 'widget-one-to-many-relation-card-progressive',
|
||||
pageLayoutTabId: TAB_ID_OVERVIEW,
|
||||
type: WidgetType.FIELD,
|
||||
title: 'People',
|
||||
objectMetadataId: companyObjectMetadataItem.id,
|
||||
gridPosition: {
|
||||
__typename: 'GridPosition',
|
||||
row: 11,
|
||||
column: 0,
|
||||
rowSpan: 1,
|
||||
columnSpan: 2,
|
||||
},
|
||||
configuration: {
|
||||
__typename: 'FieldConfiguration',
|
||||
configurationType: WidgetConfigurationType.FIELD,
|
||||
fieldMetadataId: companyPeopleField.id,
|
||||
layout: 'CARD',
|
||||
},
|
||||
createdAt: '2024-01-01T00:00:00Z',
|
||||
updatedAt: '2024-01-01T00:00:00Z',
|
||||
deletedAt: null,
|
||||
};
|
||||
|
||||
const initializeState = (snapshot: MutableSnapshot) => {
|
||||
snapshot.set(objectMetadataItemsState, generatedMockObjectMetadataItems);
|
||||
snapshot.set(shouldAppBeLoadingState, false);
|
||||
const pageLayoutData = createPageLayoutWithWidget(
|
||||
widget,
|
||||
companyObjectMetadataItem.id,
|
||||
);
|
||||
snapshot.set(
|
||||
pageLayoutPersistedComponentState.atomFamily({
|
||||
instanceId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
}),
|
||||
pageLayoutData,
|
||||
);
|
||||
snapshot.set(
|
||||
pageLayoutDraftComponentState.atomFamily({
|
||||
instanceId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
}),
|
||||
pageLayoutData,
|
||||
);
|
||||
snapshot.set(
|
||||
recordStoreFamilyState(TEST_RECORD_ID),
|
||||
companyWithManyPeople,
|
||||
);
|
||||
// Set each person record in the store
|
||||
mockPeople.forEach((person) => {
|
||||
snapshot.set(recordStoreFamilyState(person.id), person);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ width: '400px', padding: '20px' }}>
|
||||
<JestMetadataAndApolloMocksWrapper>
|
||||
<CoreClientProviderWrapper>
|
||||
<PageLayoutTestWrapper initializeState={initializeState}>
|
||||
<LayoutRenderingProvider
|
||||
value={{
|
||||
isInRightDrawer: false,
|
||||
layoutType: PageLayoutType.RECORD_PAGE,
|
||||
targetRecordIdentifier: {
|
||||
id: TEST_RECORD_ID,
|
||||
targetObjectNameSingular:
|
||||
companyObjectMetadataItem.nameSingular,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<PageLayoutContentProvider
|
||||
value={{
|
||||
layoutMode: 'vertical-list',
|
||||
tabId: 'fields',
|
||||
}}
|
||||
>
|
||||
<WidgetComponentInstanceContext.Provider
|
||||
value={{ instanceId: widget.id }}
|
||||
>
|
||||
<FieldWidget widget={widget} />
|
||||
</WidgetComponentInstanceContext.Provider>
|
||||
</PageLayoutContentProvider>
|
||||
</LayoutRenderingProvider>
|
||||
</PageLayoutTestWrapper>
|
||||
</CoreClientProviderWrapper>
|
||||
</JestMetadataAndApolloMocksWrapper>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
// Verify initial display - should show first 5 items
|
||||
const firstPerson = await canvas.findByText('Jane Smith 1');
|
||||
expect(firstPerson).toBeVisible();
|
||||
|
||||
const fifthPerson = await canvas.findByText('Carol Miller 5');
|
||||
expect(fifthPerson).toBeVisible();
|
||||
|
||||
// Verify sixth person is NOT visible initially
|
||||
expect(canvas.queryByText('David Wilson 6')).not.toBeInTheDocument();
|
||||
|
||||
// Verify "More (7)" button is visible (12 total - 5 shown = 7 remaining)
|
||||
const moreButton = await canvas.findByTestId(
|
||||
'field-widget-show-more-button',
|
||||
);
|
||||
expect(moreButton).toBeVisible();
|
||||
expect(moreButton).toHaveTextContent('More (7)');
|
||||
|
||||
// Click "More" button to load 5 more items
|
||||
await userEvent.click(moreButton);
|
||||
|
||||
// Verify more items are now visible
|
||||
await waitFor(() => {
|
||||
const sixthPerson = canvas.getByText('David Wilson 6');
|
||||
expect(sixthPerson).toBeVisible();
|
||||
});
|
||||
|
||||
const tenthPerson = await canvas.findByText('Henry Thomas 10');
|
||||
expect(tenthPerson).toBeVisible();
|
||||
|
||||
// Verify "More (2)" button is visible (12 total - 10 shown = 2 remaining)
|
||||
const updatedMoreButton = await canvas.findByTestId(
|
||||
'field-widget-show-more-button',
|
||||
);
|
||||
expect(updatedMoreButton).toHaveTextContent('More (2)');
|
||||
|
||||
// Click "More" button again to load remaining items
|
||||
await userEvent.click(updatedMoreButton);
|
||||
|
||||
// Verify all items are now visible
|
||||
await waitFor(() => {
|
||||
const twelfthPerson = canvas.getByText('Jack White 12');
|
||||
expect(twelfthPerson).toBeVisible();
|
||||
});
|
||||
|
||||
// Verify "More" button is no longer visible
|
||||
expect(
|
||||
canvas.queryByTestId('field-widget-show-more-button'),
|
||||
).not.toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
export const FIELD_WIDGET_RELATION_CARD_INITIAL_VISIBLE_ITEMS = 5;
|
||||
+1
@@ -0,0 +1 @@
|
||||
export const FIELD_WIDGET_RELATION_CARD_LOAD_MORE_INCREMENT = 5;
|
||||
Reference in New Issue
Block a user