Skip to content

Commit 849b2a4

Browse files
authored
Collapse linked sources if more than 5 entries (fixes #283) (#828)
* Collapse linked sources if more than 5 entries (fixes #283) * Fix uneven spacing and add consistent bottom margin to link containers Signed-off-by: rashmivemuri <rashmivemuri@gmail.com> --------- Signed-off-by: rashmivemuri <rashmivemuri@gmail.com>
1 parent bbe9262 commit 849b2a4

2 files changed

Lines changed: 66 additions & 47 deletions

File tree

application/frontend/src/components/DocumentNode/DocumentNode.tsx

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import './documentNode.scss';
22

33
import axios from 'axios';
44
import React, { FunctionComponent, useContext, useEffect, useMemo, useState } from 'react';
5-
import { Link, useHistory } from 'react-router-dom';
5+
import { Link, useHistory, useLocation } from 'react-router-dom';
66
import { Icon } from 'semantic-ui-react';
77

8-
import { TYPE_AUTOLINKED_TO, TYPE_CONTAINS, TYPE_IS_PART_OF, TYPE_RELATED } from '../../const';
8+
import { CRE, TYPE_AUTOLINKED_TO, TYPE_CONTAINS, TYPE_IS_PART_OF, TYPE_RELATED } from '../../const';
99
import { useEnvironment } from '../../hooks';
1010
import { applyFilters } from '../../hooks/applyFilters';
1111
import { Document } from '../../types';
@@ -14,6 +14,7 @@ import { getApiEndpoint, getDocumentTypeText, getInternalUrl } from '../../utils
1414
import { FilterButton } from '../FilterButton/FilterButton';
1515
import { LoadingAndErrorIndicator } from '../LoadingAndErrorIndicator';
1616

17+
const MAX_LENGTH_FOR_AUTO_EXPAND = 5;
1718
export interface DocumentNode {
1819
node: Document;
1920
linkType: string;
@@ -30,6 +31,7 @@ export const DocumentNode: FunctionComponent<DocumentNode> = ({
3031
hasLinktypeRelatedParent,
3132
}) => {
3233
const [expanded, setExpanded] = useState<boolean>(false);
34+
const [showAll, setShowAll] = useState<Record<number, boolean>>({});
3335
const isStandard = node.doctype in ['Tool', 'Code', 'Standard'];
3436
const { apiUrl } = useEnvironment();
3537
const [nestedNode, setNestedNode] = useState<Document>();
@@ -42,6 +44,27 @@ export const DocumentNode: FunctionComponent<DocumentNode> = ({
4244
const hasExternalLink = Boolean(usedNode.hyperlink);
4345
const linksByType = useMemo(() => groupLinksByType(usedNode), [usedNode]);
4446
let currentUrlParams = new URLSearchParams(window.location.search);
47+
const { pathname } = useLocation();
48+
const isCrePath = useMemo(() => pathname.includes(CRE), [pathname]);
49+
50+
const isNestedInRelated = hasLinktypeRelatedParent || linkType === TYPE_RELATED;
51+
52+
const getTopicsToDisplayOrderdByLinkType = () => {
53+
return Object.entries(linksByType)
54+
.filter(([type, _]) => !linkTypesExcludedInNesting.includes(type))
55+
.filter(([type, _]) =>
56+
isNestedInRelated ? !linkTypesExcludedWhenNestingRelatedTo.includes(type) : true
57+
);
58+
};
59+
60+
const topicsToDisplay = useMemo(() => getTopicsToDisplayOrderdByLinkType(), [linksByType, isNestedInRelated]);
61+
62+
useEffect(() => {
63+
const isAllowedToAutoExpandByLength =
64+
topicsToDisplay.map(([, links]) => links).reduce((prev, cur) => prev.concat(cur), []).length <=
65+
MAX_LENGTH_FOR_AUTO_EXPAND;
66+
setExpanded(isCrePath ? isAllowedToAutoExpandByLength : false);
67+
}, [topicsToDisplay, isCrePath]);
4568

4669
useEffect(() => {
4770
if (!isStandard && linkTypesToNest.includes(linkType)) {
@@ -66,19 +89,7 @@ export const DocumentNode: FunctionComponent<DocumentNode> = ({
6689
};
6790

6891
const hasActiveLinks = () => {
69-
return getTopicsToDisplayOrderdByLinkType().length > 0;
70-
};
71-
72-
const isNestedInRelated = (): Boolean => {
73-
return hasLinktypeRelatedParent || linkType === TYPE_RELATED;
74-
};
75-
76-
const getTopicsToDisplayOrderdByLinkType = () => {
77-
return Object.entries(linksByType)
78-
.filter(([type, _]) => !linkTypesExcludedInNesting.includes(type))
79-
.filter(([type, _]) =>
80-
isNestedInRelated() ? !linkTypesExcludedWhenNestingRelatedTo.includes(type) : true
81-
);
92+
return topicsToDisplay.length > 0;
8293
};
8394

8495
const Hyperlink = (hyperlink) => {
@@ -134,37 +145,39 @@ export const DocumentNode: FunctionComponent<DocumentNode> = ({
134145
<div className={`content${active} document-node`}>
135146
<Hyperlink hyperlink={usedNode.hyperlink} />
136147
{expanded &&
137-
getTopicsToDisplayOrderdByLinkType().map(([type, links], idx) => {
138-
const sortedResults = links.sort((a, b) =>
148+
topicsToDisplay.map(([type, links], idx) => {
149+
const sortedResults = [...links].sort((a, b) =>
139150
getDocumentDisplayName(a.document).localeCompare(getDocumentDisplayName(b.document))
140151
);
141-
let lastDocumentName = sortedResults[0].document.name;
142152
return (
143-
<div className="document-node__link-type-container" key={type}>
153+
<div className="document-node__link-type-container" key={`${type}-${idx}`}>
144154
{idx > 0 && <hr style={{ borderColor: 'transparent', margin: '20px 0' }} />}
145155
<div>
146156
<b>Which {getDocumentTypeText(type, links[0].document.doctype, node.doctype)}</b>:
147157
{/* Risk here of mixed doctype in here causing odd output */}
148158
</div>
149159
<div>
150160
<div className="accordion ui fluid styled f0">
151-
{sortedResults.map((link, i) => {
152-
const temp = (
153-
<div key={Math.random()}>
154-
{lastDocumentName !== link.document.name && <span style={{ margin: '5px' }} />}
155-
<DocumentNode
156-
node={link.document}
157-
linkType={type}
158-
hasLinktypeRelatedParent={isNestedInRelated()}
159-
key={Math.random()}
160-
/>
161-
<FilterButton document={link.document} />
162-
</div>
163-
);
164-
lastDocumentName = link.document.name;
165-
return temp;
166-
})}
161+
{sortedResults.slice(0, showAll[idx] ? sortedResults.length : MAX_LENGTH_FOR_AUTO_EXPAND).map((link, i) => (
162+
<div key={`document-node-container-${type}-${idx}-${i}`} style={{ marginBottom: '4px' }}>
163+
<DocumentNode
164+
node={link.document}
165+
linkType={type}
166+
hasLinktypeRelatedParent={isNestedInRelated as boolean}
167+
key={`document-sub-node-${type}-${idx}-${i}`}
168+
/>
169+
<FilterButton document={link.document} />
170+
</div>
171+
))}
167172
</div>
173+
{sortedResults.length > MAX_LENGTH_FOR_AUTO_EXPAND && (
174+
<button
175+
onClick={() => setShowAll(prev => ({ ...prev, [idx]: !prev[idx] }))}
176+
style={{ marginTop: '8px', cursor: 'pointer' }}
177+
>
178+
{showAll[idx] ? 'Show less ▲' : 'Show more ▼'}
179+
</button>
180+
)}
168181
</div>
169182
</div>
170183
);

application/frontend/src/pages/CommonRequirementEnumeration/CommonRequirementEnumeration.tsx

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,24 @@ import { Document } from '../../types';
1313
import { groupLinksByType } from '../../utils';
1414
import { getDocumentDisplayName, getDocumentTypeText, orderLinksByType } from '../../utils/document';
1515

16+
const MAX_LENGTH_FOR_AUTO_EXPAND = 5;
17+
1618
export const CommonRequirementEnumeration = () => {
1719
const { id } = useParams<{ id: string }>();
1820
const { search } = useLocation();
1921
const { apiUrl } = useEnvironment();
2022
const [loading, setLoading] = useState<boolean>(false);
2123
const [error, setError] = useState<string | Object | null>(null);
2224
const [data, setData] = useState<Document | null>();
25+
const [showAll, setShowAll] = useState<Record<string, boolean>>({});
2326
const source = useMemo(() => {
2427
const sourceParam = new URLSearchParams(search).get('source');
2528
return sourceParam && sourceParam.trim().length > 0 ? sourceParam : null;
2629
}, [search]);
2730

2831
useEffect(() => {
2932
setLoading(true);
33+
setShowAll({});
3034
window.scrollTo(0, 0);
3135

3236
const params = source ? { params: { source } } : undefined;
@@ -93,24 +97,26 @@ export const CommonRequirementEnumeration = () => {
9397
const sortedResults = links.sort((a, b) =>
9498
getDocumentDisplayName(a.document).localeCompare(getDocumentDisplayName(b.document))
9599
);
96-
let lastDocumentName = sortedResults[0].document.name;
97100
return (
98101
<div className="cre-page__links" key={type}>
99102
<div className="cre-page__links-eader">
100103
<b>Which {getDocumentTypeText(type, links[0].document.doctype)}</b>:
101104
{/* Risk of mixed doctype in here causing odd output */}
102105
</div>
103-
{sortedResults.map((link, i) => {
104-
const temp = (
105-
<div key={i} className="accordion ui fluid styled cre-page__links-container">
106-
{lastDocumentName !== link.document.name && <span style={{ margin: '5px' }} />}
107-
<DocumentNode node={link.document} linkType={type} />
108-
<FilterButton document={link.document} />
109-
</div>
110-
);
111-
lastDocumentName = link.document.name;
112-
return temp;
113-
})}
106+
{sortedResults.slice(0, showAll[type] ? sortedResults.length : MAX_LENGTH_FOR_AUTO_EXPAND).map((link, i) => (
107+
<div key={i} className="accordion ui fluid styled cre-page__links-container" style={{ marginBottom: '4px' }}>
108+
<DocumentNode node={link.document} linkType={type} />
109+
<FilterButton document={link.document} />
110+
</div>
111+
))}
112+
{sortedResults.length > MAX_LENGTH_FOR_AUTO_EXPAND && (
113+
<button
114+
onClick={() => setShowAll(prev => ({ ...prev, [type]: !prev[type] }))}
115+
style={{ marginTop: '8px', cursor: 'pointer' }}
116+
>
117+
{showAll[type] ? 'Show less ▲' : 'Show more ▼'}
118+
</button>
119+
)}
114120
</div>
115121
);
116122
})}

0 commit comments

Comments
 (0)