Skip to content

Commit 23a6a2f

Browse files
authored
Merge pull request #145 from navikt/keep-open-version-selector-2
Hold åpen versjonsvelger
2 parents 9f34794 + ba4401e commit 23a6a2f

File tree

5 files changed

+184
-28
lines changed

5 files changed

+184
-28
lines changed

xp-archive/client/content/Content.tsx

+104-22
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ import { VersionSelector } from 'client/versionSelector/VersionSelector';
99
import { ContentView } from '../contentView/ContentView';
1010
import { formatTimestamp } from '@common/shared/timestamp';
1111
import { EmptyState } from '@common/shared/EmptyState/EmptyState';
12+
import {
13+
setCachedVersionSelector,
14+
getCachedVersionSelector,
15+
clearCachedVersionSelector,
16+
} from 'client/versionSelector/VersionSelectorCache';
1217

1318
import style from './Content.module.css';
1419

@@ -18,15 +23,12 @@ const getDefaultView = (isWebpage: boolean, hasAttachment: boolean): ViewVariant
1823
return undefined;
1924
};
2025

21-
const updateContentUrl = (nodeId: string, locale: string, versionId?: string) => {
22-
const newUrl = `${xpArchiveConfig.basePath}/${nodeId}/${locale}/${versionId ?? ''}`;
23-
window.history.pushState({}, '', newUrl);
24-
};
25-
2626
export const Content = () => {
2727
const { selectedContentId, selectedLocale, selectedVersion, setSelectedVersion } =
2828
useAppState();
2929

30+
const prevContentIdRef = React.useRef(selectedContentId);
31+
3032
useEffect(() => {
3133
const pathSegments = window.location.pathname.split('/');
3234
if (pathSegments.length >= 5) {
@@ -43,21 +45,58 @@ export const Content = () => {
4345
});
4446

4547
useEffect(() => {
46-
if (selectedVersion) {
47-
updateContentUrl(selectedContentId ?? '', selectedLocale, selectedVersion);
48-
} else if (data?.versions?.[0]) {
49-
const latestVersionId = data.versions[0].versionId;
50-
setSelectedVersion(latestVersionId);
51-
updateContentUrl(selectedContentId ?? '', selectedLocale, latestVersionId);
48+
const versionId = selectedVersion ?? data?.versions?.[0]?.versionId;
49+
if (versionId) {
50+
if (!selectedVersion) {
51+
setSelectedVersion(versionId);
52+
}
53+
const newUrl = `${xpArchiveConfig.basePath}/${selectedContentId}/${selectedLocale}/${versionId}`;
54+
window.history.pushState({}, '', newUrl);
5255
}
5356
}, [data, selectedContentId, selectedLocale, selectedVersion]);
5457

55-
const isWebpage = !!data?.html && !data.json.attachment;
56-
const hasAttachment = !!data?.json.attachment;
58+
const isWebpage = !!data?.html && !data?.json?.attachment;
59+
const hasAttachment = !!data?.json?.attachment;
5760
const [selectedView, setSelectedView] = useState<ViewVariant | undefined>(
5861
getDefaultView(isWebpage, hasAttachment)
5962
);
60-
const [isVersionPanelOpen, setIsVersionPanelOpen] = useState(false);
63+
64+
const [versionSelectorCache, setVersionSelectorCache] = useState(() => {
65+
const cache = getCachedVersionSelector(selectedContentId ?? '');
66+
return {
67+
component: cache.component,
68+
versions: cache.versions,
69+
isOpen: cache.isOpen,
70+
};
71+
});
72+
73+
const [cachedDisplayData, setCachedDisplayData] = useState({
74+
displayName: '',
75+
path: '',
76+
});
77+
78+
useEffect(() => {
79+
if (prevContentIdRef.current && prevContentIdRef.current !== selectedContentId) {
80+
clearCachedVersionSelector(prevContentIdRef.current);
81+
}
82+
83+
if (data?.versions && selectedContentId) {
84+
setVersionSelectorCache((prev) => ({
85+
component: null,
86+
versions: data.versions,
87+
isOpen: prev.isOpen,
88+
}));
89+
90+
if (data.json?.displayName || data.json?._path) {
91+
setCachedDisplayData({
92+
displayName: data.json.displayName || '',
93+
path: data.json._path || '',
94+
});
95+
}
96+
}
97+
98+
prevContentIdRef.current = selectedContentId;
99+
}, [selectedContentId, data?.versions, data?.json]);
61100

62101
useEffect(() => {
63102
setSelectedView(getDefaultView(isWebpage, hasAttachment));
@@ -68,6 +107,15 @@ export const Content = () => {
68107
}`;
69108

70109
const getVersionDisplay = () => {
110+
if (selectedVersion && versionSelectorCache.versions.length > 0) {
111+
const cachedVersion = versionSelectorCache.versions.find(
112+
(v) => v.versionId === selectedVersion
113+
);
114+
if (cachedVersion?.timestamp) {
115+
return formatTimestamp(cachedVersion.timestamp);
116+
}
117+
}
118+
71119
if (selectedVersion && data?.versions) {
72120
return formatTimestamp(
73121
data.versions.find((v) => v.versionId === selectedVersion)?.timestamp ?? ''
@@ -91,15 +139,49 @@ export const Content = () => {
91139
variant={'secondary'}
92140
icon={<SidebarRightIcon />}
93141
iconPosition={'right'}
94-
onClick={() => setIsVersionPanelOpen(true)}
142+
onClick={() => {
143+
setVersionSelectorCache((prev) => ({
144+
...prev,
145+
isOpen: true,
146+
}));
147+
}}
95148
>
96149
{getVersionDisplay()}
97150
</Button>
98-
<VersionSelector
99-
versions={data?.versions || []}
100-
isOpen={isVersionPanelOpen}
101-
onClose={() => setIsVersionPanelOpen(false)}
102-
/>
151+
152+
{versionSelectorCache.component
153+
? versionSelectorCache.component
154+
: (() => {
155+
const versionSelectorVersions =
156+
versionSelectorCache.versions.length > 0
157+
? versionSelectorCache.versions
158+
: data?.versions || [];
159+
160+
const handleClose = () => {
161+
setVersionSelectorCache((prev) => ({
162+
...prev,
163+
isOpen: false,
164+
}));
165+
};
166+
167+
const handleMount = (component: React.ReactNode) => {
168+
setCachedVersionSelector(
169+
selectedContentId ?? '',
170+
component,
171+
versionSelectorVersions,
172+
versionSelectorCache.isOpen
173+
);
174+
};
175+
176+
return (
177+
<VersionSelector
178+
versions={versionSelectorVersions}
179+
isOpen={versionSelectorCache.isOpen}
180+
onClose={handleClose}
181+
onMount={handleMount}
182+
/>
183+
);
184+
})()}
103185
</div>
104186
<div className={style.viewSelector}>
105187
<Label className={style.label}>Visning</Label>
@@ -128,10 +210,10 @@ export const Content = () => {
128210

129211
<div className={style.titleAndUrl}>
130212
<Heading size={'medium'} level={'2'}>
131-
{data?.json.displayName ?? ''}
213+
{data?.json?.displayName || cachedDisplayData.displayName || 'Laster...'}
132214
</Heading>
133215
<div className={style.url}>
134-
<Detail>{data?.json._path ?? ''}</Detail>
216+
<Detail>{data?.json?._path || cachedDisplayData.path || ''}</Detail>
135217
</div>
136218
</div>
137219
</div>

xp-archive/client/versionSelector/SlidePanel/SlidePanel.module.css

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
}
1818

1919
.panel {
20+
display: flex;
21+
flex-direction: column;
2022
position: relative;
2123
z-index: 1;
22-
width: 400px;
24+
width: 460px;
2325
height: 100vh;
2426
background: var(--a-surface-default);
2527
box-shadow: 4px 0 8px rgba(0, 0, 0, 0.1);

xp-archive/client/versionSelector/VersionSelector.module.css

+11
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,14 @@
4040
font-weight: var(--a-font-weight-bold);
4141
}
4242
}
43+
44+
.closeButton {
45+
position: sticky;
46+
bottom: 1rem;
47+
margin-top: 1.75rem;
48+
49+
width: fit-content;
50+
border-radius: 99px;
51+
background-color: var(--a-surface-inverted);
52+
align-self: center;
53+
}

xp-archive/client/versionSelector/VersionSelector.tsx

+21-5
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
import React, { useState } from 'react';
1+
import React, { useState, useEffect } from 'react';
2+
import { CheckmarkIcon, XMarkIcon } from '@navikt/aksel-icons';
23
import { Heading, Button, Search } from '@navikt/ds-react';
34
import { VersionReference } from 'shared/types';
45
import { formatTimestamp } from '@common/shared/timestamp';
56
import { useAppState } from 'client/context/appState/useAppState';
67
import { SlidePanel } from './SlidePanel/SlidePanel';
78
import { classNames } from '@common/client/utils/classNames';
89
import style from './VersionSelector.module.css';
9-
import { CheckmarkIcon } from '@navikt/aksel-icons';
1010

1111
type Props = {
1212
versions: VersionReference[];
1313
isOpen: boolean;
1414
onClose: () => void;
15+
onMount?: (component: React.ReactNode) => void;
1516
};
1617

1718
type VersionButtonProps = {
@@ -32,7 +33,7 @@ const VersionButton = ({ isSelected, onClick, children }: VersionButtonProps) =>
3233
</Button>
3334
);
3435

35-
export const VersionSelector = ({ versions, isOpen, onClose }: Props) => {
36+
export const VersionSelector = ({ versions, isOpen, onClose, onMount }: Props) => {
3637
const [searchQuery, setSearchQuery] = useState('');
3738
const { setSelectedContentId, selectedVersion, setSelectedVersion } = useAppState();
3839

@@ -45,14 +46,13 @@ export const VersionSelector = ({ versions, isOpen, onClose }: Props) => {
4546
const nodeId = versions.find((v) => v.versionId === versionId)?.nodeId;
4647
if (nodeId) setSelectedContentId(nodeId);
4748
setSelectedVersion(versionId);
48-
handleClose();
4949
};
5050

5151
const filteredVersions = versions.filter((version) =>
5252
formatTimestamp(version.timestamp).toLowerCase().includes(searchQuery.toLowerCase())
5353
);
5454

55-
return (
55+
const component = (
5656
<SlidePanel isOpen={isOpen} onClose={handleClose}>
5757
<Heading size="medium" spacing>
5858
Versjoner
@@ -78,6 +78,22 @@ export const VersionSelector = ({ versions, isOpen, onClose }: Props) => {
7878
</VersionButton>
7979
))}
8080
</div>
81+
<Button
82+
className={style.closeButton}
83+
variant="primary-neutral"
84+
icon={<XMarkIcon />}
85+
onClick={handleClose}
86+
>
87+
Lukk versjonsvelger
88+
</Button>
8189
</SlidePanel>
8290
);
91+
92+
useEffect(() => {
93+
if (onMount) {
94+
onMount(component);
95+
}
96+
}, [versions, isOpen, searchQuery, selectedVersion]);
97+
98+
return component;
8399
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import React from 'react';
2+
import { VersionReference } from 'shared/types';
3+
4+
type VersionSelectorCacheItem = {
5+
component: React.ReactNode | null;
6+
versions: VersionReference[];
7+
isOpen: boolean;
8+
};
9+
10+
const DEFAULT_CACHE: VersionSelectorCacheItem = {
11+
component: null,
12+
versions: [],
13+
isOpen: false,
14+
};
15+
16+
const contentCache: Record<string, VersionSelectorCacheItem> = {};
17+
18+
export const setCachedVersionSelector = (
19+
contentId: string,
20+
component: React.ReactNode,
21+
versions: VersionReference[],
22+
isOpen: boolean
23+
) => {
24+
Object.keys(contentCache).forEach((key) => {
25+
if (key !== contentId) {
26+
delete contentCache[key];
27+
}
28+
});
29+
30+
contentCache[contentId] = {
31+
component,
32+
versions,
33+
isOpen,
34+
};
35+
};
36+
37+
export const getCachedVersionSelector = (contentId: string): VersionSelectorCacheItem => {
38+
return contentCache[contentId] || DEFAULT_CACHE;
39+
};
40+
41+
export const clearCachedVersionSelector = (contentId: string): void => {
42+
if (contentId in contentCache) {
43+
delete contentCache[contentId];
44+
}
45+
};

0 commit comments

Comments
 (0)