Skip to content

Commit

Permalink
Preview - reduce cognitive complexity
Browse files Browse the repository at this point in the history
  • Loading branch information
SKarolFolio committed Nov 22, 2024
1 parent 1cc6e76 commit 2281fab
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 98 deletions.
7 changes: 7 additions & 0 deletions src/common/constants/uiControls.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,10 @@ export enum SchemaControlType {
export const UI_CONTROLS_LIST = [AdvancedFieldType.literal, AdvancedFieldType.simple, AdvancedFieldType.complex];

export const UI_DROPDOWNS_LIST = [AdvancedFieldType.dropdown, AdvancedFieldType.dropdownOption];

export const NOT_PREVIEWABLE_TYPES = [
AdvancedFieldType.profile,
AdvancedFieldType.hidden,
AdvancedFieldType.dropdownOption,
AdvancedFieldType.complex,
];
71 changes: 71 additions & 0 deletions src/common/helpers/record.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { AUTOCLEAR_TIMEOUT } from '@common/constants/storage.constants';
import { localStorageService } from '@common/services/storage';
import { generateRecordBackupKey } from './progressBackup.helper';
import {
GROUP_BY_LEVEL,
GROUP_CONTENTS_LEVEL,
IDENTIFIER_AS_VALUE,
PROFILE_BFIDS,
TITLE_CONTAINER_URIS,
Expand All @@ -12,6 +14,8 @@ import { BFLITE_URI_TO_BLOCK, BFLITE_URIS, BLOCKS_BFLITE } from '@common/constan
import { ResourceType } from '@common/constants/record.constants';
import { QueryParams } from '@common/constants/routes.constants';
import { cloneDeep } from 'lodash';
import { NOT_PREVIEWABLE_TYPES, AdvancedFieldType } from '@common/constants/uiControls.constants';
import { PREVIEW_ALT_DISPLAY_LABELS } from '@common/constants/uiElements.constants';

type IGetAdjustedRecordContents = {
record: RecordEntry;
Expand Down Expand Up @@ -203,3 +207,70 @@ export const checkIfRecordHasDependencies = (record: RecordEntry) => {
export const getRecordPropertyData = (property: string[] | string) => {
return Array.isArray(property) ? property[0] : property;
};

export const getPreviewFieldsConditions = ({
entry,
level,
userValues,
uuid,
schema,
isOnBranchWithUserValue,
altDisplayNames,
hideActions,
isEntity,
forceRenderAllTopLevelEntities,
}: {
entry: SchemaEntry;
level: number;
userValues: UserValues;
uuid: string;
schema: Schema;
isOnBranchWithUserValue: boolean;
altDisplayNames?: Record<string, string>;
hideActions?: boolean;
isEntity: boolean;
forceRenderAllTopLevelEntities?: boolean;
}) => {
const { displayName = '', children, type, bfid = '', linkedEntry } = entry;

const isPreviewable = !NOT_PREVIEWABLE_TYPES.includes(type as AdvancedFieldType);
const isGroupable = level <= GROUP_BY_LEVEL;
const hasChildren = children?.length;
const selectedUserValues = userValues[uuid];
const isBranchEnd = !hasChildren;
const isBranchEndWithoutValues = !selectedUserValues && isBranchEnd;
const isBranchEndWithValues = !!selectedUserValues;
const shouldRenderLabelOrPlaceholders =
(isPreviewable && isGroupable) ||
type === AdvancedFieldType.dropdown ||
(isBranchEndWithValues && type !== AdvancedFieldType.complex) ||
isBranchEndWithoutValues;
const hasOnlyDropdownChildren =
hasChildren &&
!children.filter(childUuid => schema.get(childUuid)?.type !== AdvancedFieldType.dropdownOption).length;
const shouldRenderValuesOrPlaceholders = !hasChildren || hasOnlyDropdownChildren;
const shouldRenderPlaceholders =
(isPreviewable && isGroupable && !isOnBranchWithUserValue) || !isOnBranchWithUserValue;
const isDependentDropdown = type === AdvancedFieldType.dropdown && !!linkedEntry?.controlledBy;
const displayNameWithAltValue =
altDisplayNames?.[displayName] ?? PREVIEW_ALT_DISPLAY_LABELS[displayName] ?? displayName;
const isBlock = level === GROUP_BY_LEVEL && shouldRenderLabelOrPlaceholders;
const isBlockContents = level === GROUP_CONTENTS_LEVEL;
const isInstance = bfid === PROFILE_BFIDS.INSTANCE;
const showEntityActions = !hideActions && isEntity;
const wrapEntities = forceRenderAllTopLevelEntities && isEntity;

return {
isGroupable,
shouldRenderLabelOrPlaceholders,
shouldRenderValuesOrPlaceholders,
shouldRenderPlaceholders,
isDependentDropdown,
displayNameWithAltValue,
isBlock,
isBlockContents,
isInstance,
showEntityActions,
wrapEntities,
};
};
149 changes: 51 additions & 98 deletions src/components/Preview/Fields.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,18 @@
import { ReactNode } from 'react';
import { FormattedMessage } from 'react-intl';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import classNames from 'classnames';
import {
ENTITY_LEVEL,
GROUP_BY_LEVEL,
GROUP_CONTENTS_LEVEL,
PROFILE_BFIDS,
RESOURCE_TEMPLATE_IDS,
} from '@common/constants/bibframe.constants';
import { BFLITE_BFID_TO_BLOCK, BLOCKS_BFLITE } from '@common/constants/bibframeMapping.constants';
import { ENTITY_LEVEL, GROUP_BY_LEVEL } from '@common/constants/bibframe.constants';
import { BFLITE_BFID_TO_BLOCK } from '@common/constants/bibframeMapping.constants';
import { RecordStatus } from '@common/constants/record.constants';
import { AdvancedFieldType } from '@common/constants/uiControls.constants';
import { PREVIEW_ALT_DISPLAY_LABELS } from '@common/constants/uiElements.constants';
import { generateEditResourceUrl } from '@common/helpers/navigation.helper';
import { getRecordId } from '@common/helpers/record.helper';
import { getRecordId, getPreviewFieldsConditions } from '@common/helpers/record.helper';
import { getParentEntryUuid } from '@common/helpers/schema.helper';
import { useNavigateToEditPage } from '@common/hooks/useNavigateToEditPage';
import { Button, ButtonType } from '@components/Button';
import { ConditionalWrapper } from '@components/ConditionalWrapper';
import { PreviewActionsDropdown } from '@components/PreviewActionsDropdown';
import Lightbulb16 from '@src/assets/lightbulb-shining-16.svg?react';
import state from '@state';

const NOT_PREVIEWABLE_TYPES = [
AdvancedFieldType.profile,
AdvancedFieldType.hidden,
AdvancedFieldType.dropdownOption,
AdvancedFieldType.complex,
];
import { Labels } from './Labels';
import { Values } from './Values';

const checkShouldGroupWrap = (level: number, entry = {} as SchemaEntry) => {
const { children, type } = entry;
Expand Down Expand Up @@ -117,7 +101,7 @@ export const Fields = ({

if (!entry) return null;

const { displayName = '', children, type, bfid = '', path, linkedEntry, htmlId } = entry;
const { displayName = '', children, type, bfid = '', path, htmlId } = entry;
const isDependentDropdownOption =
type === AdvancedFieldType.dropdownOption && !!schema.get(getParentEntryUuid(path))?.linkedEntry?.controlledBy;
const visibleDropdownOption =
Expand All @@ -131,90 +115,59 @@ export const Fields = ({
// don't render top level entities not selected for preview
if (isEntity && !currentlyPreviewedEntityBfid.has(bfid) && !forceRenderAllTopLevelEntities) return null;

const isPreviewable = !NOT_PREVIEWABLE_TYPES.includes(type as AdvancedFieldType);
const isGroupable = level <= GROUP_BY_LEVEL;
const hasChildren = children?.length;
const selectedUserValues = userValues[uuid];
const isBranchEnd = !hasChildren;
const isBranchEndWithoutValues = !selectedUserValues && isBranchEnd;
const isBranchEndWithValues = !!selectedUserValues;
const shouldRenderLabelOrPlaceholders =
(isPreviewable && isGroupable) ||
type === AdvancedFieldType.dropdown ||
(isBranchEndWithValues && type !== AdvancedFieldType.complex) ||
isBranchEndWithoutValues;
const hasOnlyDropdownChildren =
hasChildren &&
!children.filter(childUuid => schema.get(childUuid)?.type !== AdvancedFieldType.dropdownOption).length;
const shouldRenderValuesOrPlaceholders = !hasChildren || hasOnlyDropdownChildren;
const shouldRenderPlaceholders =
(isPreviewable && isGroupable && !isOnBranchWithUserValue) || !isOnBranchWithUserValue;
const isDependentDropdown = type === AdvancedFieldType.dropdown && !!linkedEntry?.controlledBy;
const displayNameWithAltValue =
altDisplayNames?.[displayName] ?? PREVIEW_ALT_DISPLAY_LABELS[displayName] ?? displayName;
const isBlock = level === GROUP_BY_LEVEL && shouldRenderLabelOrPlaceholders;
const isBlockContents = level === GROUP_CONTENTS_LEVEL;
const isInstance = bfid === PROFILE_BFIDS.INSTANCE;
const showEntityActions = !hideActions && isEntity;
const wrapEntities = forceRenderAllTopLevelEntities && isEntity;
const {
isGroupable,
shouldRenderLabelOrPlaceholders,
shouldRenderValuesOrPlaceholders,
shouldRenderPlaceholders,
isDependentDropdown,
displayNameWithAltValue,
isBlock,
isBlockContents,
isInstance,
showEntityActions,
wrapEntities,
} = getPreviewFieldsConditions({
entry,
level,
userValues,
uuid,
schema,
isOnBranchWithUserValue,
altDisplayNames,
hideActions,
isEntity,
forceRenderAllTopLevelEntities,
});

return (
<ConditionalWrapper
condition={isBlock || isBlockContents || wrapEntities}
wrapper={getPreviewWrapper({ isBlock, isBlockContents, wrapEntities })}
>
{shouldRenderLabelOrPlaceholders && (
<strong
className={classNames({
'entity-heading': isEntity,
'sub-heading': isBlock,
'value-heading': !isGroupable,
})}
>
{isEntity && !isInstance && !altDisplayNames && <Lightbulb16 />}
{displayNameWithAltValue}
{showEntityActions && !isInstance && (
<Button type={ButtonType.Primary} className="toggle-entity-edit" onClick={handleNavigateToEditPage}>
<FormattedMessage id={`ld.edit${RESOURCE_TEMPLATE_IDS[bfid]}`} />
</Button>
)}
{showEntityActions && isInstance && (
<PreviewActionsDropdown
ownId={getRecordId(record, BLOCKS_BFLITE.WORK.uri, BLOCKS_BFLITE.INSTANCE.referenceKey)}
referenceId={getRecordId(record, BLOCKS_BFLITE.WORK.uri)}
entityType={BLOCKS_BFLITE.INSTANCE.resourceType}
handleNavigateToEditPage={handleNavigateToEditPage}
/>
)}
</strong>
<Labels
isEntity={isEntity}
isBlock={isBlock}
isGroupable={isGroupable}
isInstance={isInstance}
altDisplayNames={altDisplayNames}
displayNameWithAltValue={displayNameWithAltValue}
showEntityActions={showEntityActions}
bfid={bfid}
handleNavigateToEditPage={handleNavigateToEditPage}
record={record}
/>
)}
{shouldRenderValuesOrPlaceholders && (
<Values
userValues={userValues}
uuid={uuid}
shouldRenderPlaceholders={shouldRenderPlaceholders}
isDependentDropdown={isDependentDropdown}
htmlId={htmlId}
/>
)}
{shouldRenderValuesOrPlaceholders &&
(userValues[uuid]
? userValues[uuid]?.contents?.map(({ label, meta: { uri, parentUri, basicLabel } = {} } = {}) => {
if (!label && !basicLabel) return;

const selectedLabel = basicLabel ?? label;

return (
selectedLabel && (
<div key={`${selectedLabel}${uri}`}>
{uri || parentUri ? (
<a className="preview-value-link" target="blank" href={uri ?? parentUri}>
{selectedLabel}
</a>
) : (
<>{selectedLabel}</>
)}
</div>
)
);
})
: shouldRenderPlaceholders &&
!isDependentDropdown && (
<div id={htmlId} className="value-group-wrapper">
-
</div>
))}
{children?.map(uuid => {
const schemaEntry = schema.get(uuid);

Expand Down
61 changes: 61 additions & 0 deletions src/components/Preview/Labels.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { FC } from 'react';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import Lightbulb16 from '@src/assets/lightbulb-shining-16.svg?react';
import { RESOURCE_TEMPLATE_IDS } from '@common/constants/bibframe.constants';
import { BLOCKS_BFLITE } from '@common/constants/bibframeMapping.constants';
import { getRecordId } from '@common/helpers/record.helper';
import { Button, ButtonType } from '@components/Button';
import { PreviewActionsDropdown } from '@components/PreviewActionsDropdown';

type LabelProps = {
isEntity: boolean;
isBlock: boolean;
isGroupable: boolean;
isInstance: boolean;
altDisplayNames?: Record<string, string>;
displayNameWithAltValue: string;
showEntityActions: boolean;
bfid: string;
handleNavigateToEditPage: VoidFunction;
record: RecordEntry<RecursiveRecordSchema> | null;
};

export const Labels: FC<LabelProps> = ({
isEntity,
isBlock,
isGroupable,
isInstance,
altDisplayNames,
displayNameWithAltValue,
showEntityActions,
bfid,
handleNavigateToEditPage,
record,
}) => {
return (
<strong
className={classNames({
'entity-heading': isEntity,
'sub-heading': isBlock,
'value-heading': !isGroupable,
})}
>
{isEntity && !isInstance && !altDisplayNames && <Lightbulb16 />}
{displayNameWithAltValue}
{showEntityActions && !isInstance && (
<Button type={ButtonType.Primary} className="toggle-entity-edit" onClick={handleNavigateToEditPage}>
<FormattedMessage id={`ld.edit${RESOURCE_TEMPLATE_IDS[bfid]}`} />
</Button>
)}
{showEntityActions && isInstance && (
<PreviewActionsDropdown
ownId={getRecordId(record, BLOCKS_BFLITE.WORK.uri, BLOCKS_BFLITE.INSTANCE.referenceKey)}
referenceId={getRecordId(record, BLOCKS_BFLITE.WORK.uri)}
entityType={BLOCKS_BFLITE.INSTANCE.resourceType}
handleNavigateToEditPage={handleNavigateToEditPage}
/>
)}
</strong>
);
};
43 changes: 43 additions & 0 deletions src/components/Preview/Values.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { FC } from 'react';

type ValuesProps = {
userValues: UserValues;
uuid: string;
shouldRenderPlaceholders: boolean;
isDependentDropdown: boolean;
htmlId?: string;
};

export const Values: FC<ValuesProps> = ({
userValues,
uuid,
shouldRenderPlaceholders,
isDependentDropdown,
htmlId,
}) => {
return userValues[uuid]
? userValues[uuid]?.contents?.map(({ label, meta: { uri, parentUri, basicLabel } = {} } = {}) => {
if (!label && !basicLabel) return;

const selectedLabel = basicLabel ?? label;

return (
selectedLabel && (
<div key={`${selectedLabel}${uri}`}>
{uri || parentUri ? (
<a className="preview-value-link" target="blank" href={uri ?? parentUri}>
{selectedLabel}
</a>
) : (
<>{selectedLabel}</>
)}
</div>
)
);
})
: shouldRenderPlaceholders && !isDependentDropdown && (
<div id={htmlId} className="value-group-wrapper">
-
</div>
);
};

0 comments on commit 2281fab

Please sign in to comment.