Skip to content

Commit be33be3

Browse files
FBanfiJuliRossi
andauthored
Bulk-Edit: field editors for initial default appearances [MAPS-53] (#10159)
* Keyboard accesibility for bulk edit [MAPS-29] (#10110) * Bulk-Edit-App: Fix sorting error and edit button with no padding [INTEG-3103] (#10090) * Bulk-Edit-App: Freeze top row with Field Names [INTEG-2953] (#10076) * freeze top row with Field Names * removing unused import * making the status column sticky too (#10082) * not showing the edit button when loading entries * fix error when changing sorting * Bulk edit: Filter columns [INTEG-3089] (#10089) * Bulk-Edit-App: Freeze top row with Field Names [INTEG-2953] (#10076) * freeze top row with Field Names * removing unused import * making the status column sticky too (#10082) * wip * select all * Refactor FilterColumns and SortMenu components for improved layout and functionality * Fixing states and enhancing performance in the process by not calling the getContentType each time * Fix box issue * Renaming and fixing warnings * sticky * corrections PR comments * Fixing rebase conflicts --------- Co-authored-by: Franco Banfi <62450599+FBanfi@users.noreply.github.com> Co-authored-by: francobanfi <franco.banfi@external.contentful.com> * wip * Added new useKeyboardNavigation hook to encapsulate keyboard navigation logic. * Simplifying a bit * Refactor keyboard navigation logic in useKeyboardNavigation hook for improved readability and performance. Simplified moveFocus and extendFocusToEdge functions by removing unnecessary useCallback and enhancing selection handling. * Refactor EntryTable and TableHeader components to improve keyboard navigation and selection handling. Updated focus logic to use HEADERS_ROW constant for better readability and maintainability. Enhanced checkbox toggle functionality for header and row selections. * fixing issues * changing styles for keyboard accessibility * Refactors and tests * Fixing focus on first cell and edge navigation * Readding column selection * Refactoring styles * Refactor Table components to centralize cell focus and selection logic. * Fixing checked disable checkboxes * Refactor EntryTable, TableHeader, and TableRow components to unify cell focus and selection logic. Updated function signatures to use FocusPosition for better clarity and maintainability. Enhanced keyboard navigation handling in useKeyboardNavigation hook and corresponding tests. * Simplifying a few things. Enter doesn't do that much anymore * fix merge --------- Co-authored-by: Franco Banfi <62450599+FBanfi@users.noreply.github.com> Co-authored-by: francobanfi <franco.banfi@external.contentful.com> * Tooltip fix for bulk edit [MAPS-57] (#10148) * fix * wip * Workaround to make both things work * changing some icons to match field editors versions + adding initial editors * changing icon + modifying tests * adding tests for FieldEditor component * adding more field editors * adding test for entry utils content type mapper * renaming method * removing unused variable * moving methods to entry utils file * removing any * removing unnecessary logic for field editor setter * changing disabled logic * refactors + removing createLocales * using field locale instead of default * removing manual numeric validations * removing mock from BulkEditModal.test.tsx * fixing tests between conflicts * Bulk-Edit: Parsing the value for the different editors [MAPS-53] (#10173) * Keyboard accesibility for bulk edit [MAPS-29] (#10110) * Bulk-Edit-App: Fix sorting error and edit button with no padding [INTEG-3103] (#10090) * Bulk-Edit-App: Freeze top row with Field Names [INTEG-2953] (#10076) * freeze top row with Field Names * removing unused import * making the status column sticky too (#10082) * not showing the edit button when loading entries * fix error when changing sorting * Bulk edit: Filter columns [INTEG-3089] (#10089) * Bulk-Edit-App: Freeze top row with Field Names [INTEG-2953] (#10076) * freeze top row with Field Names * removing unused import * making the status column sticky too (#10082) * wip * select all * Refactor FilterColumns and SortMenu components for improved layout and functionality * Fixing states and enhancing performance in the process by not calling the getContentType each time * Fix box issue * Renaming and fixing warnings * sticky * corrections PR comments * Fixing rebase conflicts --------- Co-authored-by: Franco Banfi <62450599+FBanfi@users.noreply.github.com> Co-authored-by: francobanfi <franco.banfi@external.contentful.com> * wip * Added new useKeyboardNavigation hook to encapsulate keyboard navigation logic. * Simplifying a bit * Refactor keyboard navigation logic in useKeyboardNavigation hook for improved readability and performance. Simplified moveFocus and extendFocusToEdge functions by removing unnecessary useCallback and enhancing selection handling. * Refactor EntryTable and TableHeader components to improve keyboard navigation and selection handling. Updated focus logic to use HEADERS_ROW constant for better readability and maintainability. Enhanced checkbox toggle functionality for header and row selections. * fixing issues * changing styles for keyboard accessibility * Refactors and tests * Fixing focus on first cell and edge navigation * Readding column selection * Refactoring styles * Refactor Table components to centralize cell focus and selection logic. * Fixing checked disable checkboxes * Refactor EntryTable, TableHeader, and TableRow components to unify cell focus and selection logic. Updated function signatures to use FocusPosition for better clarity and maintainability. Enhanced keyboard navigation handling in useKeyboardNavigation hook and corresponding tests. * Simplifying a few things. Enter doesn't do that much anymore * fix merge --------- Co-authored-by: Franco Banfi <62450599+FBanfi@users.noreply.github.com> Co-authored-by: francobanfi <franco.banfi@external.contentful.com> * Tooltip fix for bulk edit [MAPS-57] (#10148) * fix * wip * Workaround to make both things work * adding more field editors * adding test for entry utils content type mapper * refactors + removing createLocales * parsing the value for the different editors * setting truncation to display fields * adding more tests * pr comments * format changes between conflicts resolution * changing utils file name --------- Co-authored-by: JuliRossi <juliana.rossi@external.contentful.com> --------- Co-authored-by: JuliRossi <juliana.rossi@external.contentful.com>
1 parent 4337f57 commit be33be3

File tree

17 files changed

+8342
-393
lines changed

17 files changed

+8342
-393
lines changed

apps/bulk-edit/package-lock.json

Lines changed: 7572 additions & 185 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/bulk-edit/package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,17 @@
55
"dependencies": {
66
"@contentful/app-sdk": "^4.29.1",
77
"@contentful/f36-components": "4.79.1",
8+
"@contentful/f36-icons": "^5.6.0",
89
"@contentful/f36-multiselect": "^4.81.1",
910
"@contentful/f36-navlist": "^4.1.0-alpha.1",
1011
"@contentful/f36-tokens": "4.2.0",
11-
"@contentful/field-editor-json": "^3.3.38",
12+
"@contentful/field-editor-boolean": "^1.7.16",
13+
"@contentful/field-editor-date": "^1.9.16",
14+
"@contentful/field-editor-json": "^3.5.16",
15+
"@contentful/field-editor-multiple-line": "^1.3.9",
16+
"@contentful/field-editor-number": "^1.5.16",
17+
"@contentful/field-editor-single-line": "^1.3.9",
18+
"@contentful/field-editor-tags": "^1.7.16",
1219
"@contentful/react-apps-toolkit": "1.2.16",
1320
"@contentful/rich-text-html-renderer": "^17.1.0",
1421
"@phosphor-icons/react": "^2.1.10",

apps/bulk-edit/src/locations/Page/components/BulkEditModal.tsx

Lines changed: 24 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,18 @@
11
import React, { useEffect, useState } from 'react';
2-
import {
3-
Modal,
4-
Button,
5-
TextInput,
6-
Text,
7-
Flex,
8-
FormControl,
9-
Note,
10-
} from '@contentful/f36-components';
11-
import type { Entry, ContentTypeField } from '../types';
12-
import { getEntryFieldValue, truncate } from '../utils/entryUtils';
2+
import { Button, Flex, FormControl, Modal, Note, Text } from '@contentful/f36-components';
3+
import type { ContentTypeField, Entry } from '../types';
4+
import { formatValueForDisplay, getEntryFieldValue } from '../utils/entryUtils';
135
import { ClockIcon } from '@contentful/f36-icons';
6+
import { FieldEditor } from './FieldEditor';
7+
import type { LocalesAPI } from '@contentful/field-editor-shared';
148

159
interface BulkEditModalProps {
1610
isOpen: boolean;
1711
onClose: () => void;
1812
onSave: (newValue: string | number) => void;
1913
selectedEntries: Entry[];
2014
selectedField: ContentTypeField | null;
21-
defaultLocale: string;
15+
locales: LocalesAPI;
2216
isSaving: boolean;
2317
totalUpdateCount: number;
2418
editionCount: number;
@@ -30,23 +24,20 @@ export const BulkEditModal: React.FC<BulkEditModalProps> = ({
3024
onSave,
3125
selectedEntries,
3226
selectedField,
33-
defaultLocale,
27+
locales,
3428
isSaving,
3529
totalUpdateCount,
3630
editionCount,
3731
}) => {
38-
const [value, setValue] = useState('');
32+
const [value, setValue] = useState<any>('');
3933
const entryCount = selectedEntries.length;
4034
const firstEntry = selectedEntries[0];
4135
const firstValueToUpdate =
42-
firstEntry && selectedField && defaultLocale
43-
? getEntryFieldValue(firstEntry, selectedField, defaultLocale)
36+
firstEntry && selectedField && locales.default
37+
? getEntryFieldValue(firstEntry, selectedField, locales.default)
4438
: '';
4539
const title = entryCount === 1 ? 'Edit' : 'Bulk edit';
4640

47-
const isNumber = selectedField?.type === 'Number' || selectedField?.type === 'Integer';
48-
const isInvalid = selectedField?.type === 'Integer' && !Number.isInteger(Number(value));
49-
5041
useEffect(() => {
5142
setValue('');
5243
}, [isOpen]);
@@ -66,24 +57,21 @@ export const BulkEditModal: React.FC<BulkEditModalProps> = ({
6657
</Text>
6758
<Flex>
6859
<Text>
69-
<Text fontWeight="fontWeightDemiBold">{truncate(firstValueToUpdate, 100)}</Text>{' '}
60+
<Text fontWeight="fontWeightDemiBold">
61+
{formatValueForDisplay(firstValueToUpdate, 30)}
62+
</Text>{' '}
7063
{entryCount === 1 ? 'selected' : `selected and ${entryCount - 1} more`}
7164
</Text>
7265
</Flex>
73-
<FormControl isInvalid={isInvalid}>
74-
<TextInput
75-
name="bulk-edit-value"
76-
value={value}
77-
onChange={(e) => setValue(e.target.value)}
78-
placeholder="Enter your new value"
79-
type={isNumber ? 'number' : 'text'}
80-
isInvalid={isInvalid}
81-
autoFocus
82-
/>
83-
{isInvalid && (
84-
<FormControl.ValidationMessage>
85-
Integer field does not allow decimal
86-
</FormControl.ValidationMessage>
66+
<FormControl>
67+
{selectedField && (
68+
<FieldEditor
69+
field={selectedField}
70+
value={value}
71+
onChange={setValue}
72+
locales={locales}
73+
datatest-id="field-editor"
74+
/>
8775
)}
8876
</FormControl>
8977
</Flex>
@@ -103,12 +91,8 @@ export const BulkEditModal: React.FC<BulkEditModalProps> = ({
10391
</Button>
10492
<Button
10593
variant="primary"
106-
onClick={() => {
107-
if (isInvalid) return;
108-
const finalValue = isNumber ? Number(value) : value;
109-
onSave(finalValue);
110-
}}
111-
isDisabled={!value || isInvalid}
94+
onClick={() => onSave(value)}
95+
isDisabled={value === ''}
11296
testId="bulk-edit-save"
11397
isLoading={isSaving}>
11498
Save

apps/bulk-edit/src/locations/Page/components/EntryTable.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
import React, { useState, useMemo, useCallback, useEffect, useRef } from 'react';
2-
import { Table, Box, Pagination } from '@contentful/f36-components';
1+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2+
import { Box, Pagination, Table } from '@contentful/f36-components';
33
import { useVirtualizer } from '@tanstack/react-virtual';
4-
import { Entry, ContentTypeField } from '../types';
4+
import { ContentTypeField, Entry } from '../types';
55
import { ContentTypeProps } from 'contentful-management';
66
import { styles } from '../styles';
77
import { TableHeader } from './TableHeader';
88
import { TableRow } from './TableRow';
9-
import { isCheckboxAllowed as isBulkEditable, getEntryUrl } from '../utils/entryUtils';
9+
import { getEntryUrl, isCheckboxAllowed as isBulkEditable } from '../utils/entryUtils';
1010
import {
1111
DISPLAY_NAME_COLUMN,
1212
DISPLAY_NAME_INDEX,
1313
ENTRY_STATUS_COLUMN,
1414
ESTIMATED_ROW_HEIGHT,
1515
HEADERS_ROW,
1616
} from '../utils/constants';
17-
import { useKeyboardNavigation, FocusPosition } from '../hooks/useKeyboardNavigation';
17+
import { FocusPosition, useKeyboardNavigation } from '../hooks/useKeyboardNavigation';
1818
import { tableStyles } from './EntryTable.styles';
1919

2020
interface EntryTableProps {
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import React, { useEffect, useMemo, useState } from 'react';
2+
import { SingleLineEditor } from '@contentful/field-editor-single-line';
3+
import { MultipleLineEditor } from '@contentful/field-editor-multiple-line';
4+
import { NumberEditor } from '@contentful/field-editor-number';
5+
import { DateEditor } from '@contentful/field-editor-date';
6+
import { TagsEditor } from '@contentful/field-editor-tags';
7+
import { BooleanEditor } from '@contentful/field-editor-boolean';
8+
import { JsonEditor } from '@contentful/field-editor-json';
9+
import type { ContentTypeField } from '../types';
10+
import { Note } from '@contentful/f36-components';
11+
import { i18n } from '@lingui/core';
12+
import { createFieldAPI } from '../utils/fieldEditorUtils';
13+
import type { LocalesAPI } from '@contentful/field-editor-shared';
14+
15+
interface FieldEditorProps {
16+
field: ContentTypeField;
17+
value: string;
18+
onChange: (value: string) => void;
19+
locales: LocalesAPI;
20+
}
21+
22+
const ERROR_MESSAGE = 'Failed to initialize field editor. Please try again.';
23+
export const FieldEditor: React.FC<FieldEditorProps> = ({ field, value, onChange, locales }) => {
24+
const [error, setError] = useState('');
25+
const locale = field.locale ? field.locale : locales.default;
26+
27+
// Ensure Lingui i18n is activated for editors that rely on it (e.g., JsonEditor)
28+
useEffect(() => {
29+
if (!i18n.locale) {
30+
try {
31+
const locale = field.locale ? field.locale : locales.default;
32+
i18n.load(locale, {});
33+
i18n.activate(locale);
34+
setError('');
35+
} catch (error) {
36+
setError(ERROR_MESSAGE);
37+
}
38+
}
39+
}, []);
40+
41+
const fieldApi = useMemo(() => {
42+
return createFieldAPI(field, value, onChange, locale);
43+
}, [field, value, onChange, locales.default]);
44+
45+
const renderEditor = () => {
46+
try {
47+
switch (field.type) {
48+
case 'Symbol':
49+
return (
50+
<SingleLineEditor
51+
isInitiallyDisabled={false}
52+
withCharValidation={true}
53+
field={fieldApi}
54+
locales={locales}
55+
/>
56+
);
57+
case 'Text':
58+
return (
59+
<MultipleLineEditor field={fieldApi} locales={locales} isInitiallyDisabled={false} />
60+
);
61+
case 'Number':
62+
case 'Integer':
63+
return <NumberEditor field={fieldApi} isInitiallyDisabled={false} />;
64+
case 'Date':
65+
return <DateEditor field={fieldApi} isInitiallyDisabled={false} />;
66+
case 'Array':
67+
// Short text array: items.type === 'Symbol'
68+
return <TagsEditor field={fieldApi} isInitiallyDisabled={false} />;
69+
case 'Boolean':
70+
return <BooleanEditor field={fieldApi} isInitiallyDisabled={false} />;
71+
case 'Object':
72+
return <JsonEditor field={fieldApi} isInitiallyDisabled={false} />;
73+
default:
74+
return <Note variant="negative">{ERROR_MESSAGE}</Note>;
75+
}
76+
} catch (error) {
77+
setError(ERROR_MESSAGE);
78+
console.error('Error: ', error);
79+
}
80+
};
81+
82+
if (error) {
83+
return <Note variant="negative">{error}</Note>;
84+
}
85+
86+
return <>{renderEditor()}</>;
87+
};

apps/bulk-edit/src/locations/Page/components/SearchBar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useState, useEffect, useRef } from 'react';
22
import { Flex, Text, TextInput } from '@contentful/f36-components';
3-
import { SearchIcon } from '@contentful/f36-icons';
3+
import { MagnifyingGlassIcon } from '@contentful/f36-icons';
44
import { useDebounce } from 'use-debounce';
55
import { styles } from './SearchBar.styles';
66

@@ -41,7 +41,7 @@ export const SearchBar: React.FC<SearchBarProps> = ({
4141
placeholder="Search"
4242
value={inputValue}
4343
onChange={(e) => setInputValue(e.target.value)}
44-
icon={<SearchIcon />}
44+
icon={<MagnifyingGlassIcon />}
4545
isDisabled={isDisabled}
4646
/>
4747
</Flex>

apps/bulk-edit/src/locations/Page/components/TableRow.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import React, { useEffect, useState } from 'react';
1+
import React from 'react';
22
import { Table, TextLink, Badge, Checkbox, Flex, Text } from '@contentful/f36-components';
3-
import { ExternalLinkIcon } from '@contentful/f36-icons';
3+
import { ArrowSquareOutIcon } from '@contentful/f36-icons';
44
import { Entry, ContentTypeField } from '../types';
55
import { ContentTypeProps } from 'contentful-management';
66
import { rowStyles } from './TableRow.styles';
@@ -88,7 +88,7 @@ export const TableRow: React.FC<TableRowProps> = ({
8888
target="_blank"
8989
rel="noopener noreferrer"
9090
testId="entry-link"
91-
icon={<ExternalLinkIcon />}
91+
icon={<ArrowSquareOutIcon />}
9292
alignIcon="end"
9393
tabIndex={-1}>
9494
{getEntryTitle(entry, contentType, defaultLocale)}

0 commit comments

Comments
 (0)