Skip to content

Commit 45679a1

Browse files
committed
La til taksonomisøk
1 parent df60c81 commit 45679a1

File tree

2 files changed

+409
-8
lines changed

2 files changed

+409
-8
lines changed
+389
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,389 @@
1+
import { useState, useMemo } from 'react';
2+
import {
3+
Heading,
4+
VStack,
5+
Box,
6+
Search,
7+
Label,
8+
Tag,
9+
Accordion,
10+
Table,
11+
} from '@navikt/ds-react';
12+
13+
// Define the component data structure
14+
const akselComponentEvents = [
15+
{
16+
component: "Accordion",
17+
events: [
18+
{ name: "accordion åpnet", details: { komponentId: "string", tittelTekst: "string" } },
19+
{ name: "accordion lukket", details: { komponentId: "string", tittelTekst: "string" } }
20+
]
21+
},
22+
{
23+
component: "Alert",
24+
events: [
25+
{ name: "alert lukket", details: { alertType: "string", alertVariant: "string" } }
26+
]
27+
},
28+
{
29+
component: "Button",
30+
events: [
31+
{ name: "knapp klikket", details: { knappTekst: "string", knappType: "string", knappVariant: "string" } }
32+
]
33+
},
34+
{
35+
component: "Chat",
36+
events: [
37+
{ name: "chat åpnet", details: { chatId: "string" } },
38+
{ name: "chat lukket", details: { chatId: "string" } },
39+
{ name: "chat melding sendt", details: { chatId: "string" } }
40+
]
41+
},
42+
{
43+
component: "Checkbox",
44+
events: [
45+
{ name: "avkrysningsboks valgt", details: { sjekkboksId: "string", sjekkboksTekst: "string", sjekkboksVerdi: "boolean" } }
46+
]
47+
},
48+
{
49+
component: "Chips",
50+
events: [
51+
{ name: "chip valgt", details: { chipTekst: "string", chipVerdi: "string" } },
52+
{ name: "chip fjernet", details: { chipTekst: "string", chipVerdi: "string" } }
53+
]
54+
},
55+
// More components follow the same pattern
56+
{
57+
component: "ComboboxBeta",
58+
events: [
59+
{ name: "combobox valg endret", details: { valgtVerdi: "string", valgtTekst: "string" } }
60+
]
61+
},
62+
{
63+
component: "ConfirmationPanel",
64+
events: [
65+
{ name: "bekreftelsespanel huket av", details: { panelId: "string", panelTekst: "string" } },
66+
{ name: "bekreftelsespanel avhuket", details: { panelId: "string", panelTekst: "string" } }
67+
]
68+
},
69+
{
70+
component: "CopyButton",
71+
events: [
72+
{ name: "tekst kopiert", details: { kopieringsTekst: "string" } }
73+
]
74+
},
75+
{
76+
component: "DatePicker",
77+
events: [
78+
{ name: "dato valgt", details: { datoVerdi: "string", datoFelt: "string" } }
79+
]
80+
},
81+
{
82+
component: "Dropdown",
83+
events: [
84+
{ name: "dropdown åpnet", details: { dropdownId: "string" } },
85+
{ name: "dropdown lukket", details: { dropdownId: "string" } },
86+
{ name: "dropdown valg valgt", details: { valgtVerdi: "string", valgtTekst: "string" } }
87+
]
88+
},
89+
{
90+
component: "ExpansionCard",
91+
events: [
92+
{ name: "utvidbart kort åpnet", details: { kortId: "string", kortTittel: "string" } },
93+
{ name: "utvidbart kort lukket", details: { kortId: "string", kortTittel: "string" } }
94+
]
95+
},
96+
{
97+
component: "FileUpload",
98+
events: [
99+
{ name: "fil lastet opp", details: { filNavn: "string", filStørrelse: "number", filType: "string" } },
100+
{ name: "fil fjernet", details: { filNavn: "string" } }
101+
]
102+
},
103+
{
104+
component: "Modal",
105+
events: [
106+
{ name: "modal åpnet", details: { modalId: "string", modalTittel: "string" } },
107+
{ name: "modal lukket", details: { modalId: "string", modalTittel: "string", lukkMetode: "string" } }
108+
]
109+
},
110+
{
111+
component: "ReadMore",
112+
events: [
113+
{ name: "les mer åpnet", details: { lesMerId: "string", lesMerTittel: "string" } },
114+
{ name: "les mer lukket", details: { lesMerId: "string", lesMerTittel: "string" } }
115+
]
116+
},
117+
{
118+
component: "Search",
119+
events: [
120+
{ name: "søk gjennomført", details: { søkeTekst: "string", søkeResultater: "number" } },
121+
{ name: "søkeforslag valgt", details: { valgtForslag: "string" } }
122+
]
123+
},
124+
{
125+
component: "Select",
126+
events: [
127+
{ name: "nedtrekksliste valg endret", details: { valgtVerdi: "string", valgtTekst: "string", listeId: "string" } }
128+
]
129+
},
130+
{
131+
component: "Tabs",
132+
events: [
133+
{ name: "fane byttet", details: { faneId: "string", faneTekst: "string", fraFane: "string", tilFane: "string" } }
134+
]
135+
},
136+
{
137+
component: "TextField",
138+
events: [
139+
{ name: "tekstfelt utfylt", details: { feltId: "string", feltNavn: "string", harVerdi: "boolean" } }
140+
]
141+
},
142+
];
143+
144+
const getExampleValue = (key: string, componentName: string) => {
145+
switch (key) {
146+
// IDs
147+
case 'komponentId': return `${componentName.toLowerCase()}-1`;
148+
case 'modalId': return 'bekreftelses-modal';
149+
case 'panelId': return 'informasjons-panel';
150+
case 'faneId': return 'skjema-oversikt';
151+
case 'chatId': return 'bruker-hjelp';
152+
case 'sjekkboksId': return 'godta-vilkaar';
153+
case 'feltId': return 'bruker-epost';
154+
case 'dropdownId': return 'velg-kategori';
155+
case 'lesMerId': return 'mer-info';
156+
157+
// Text content
158+
case 'knappTekst': return 'Send skjema';
159+
case 'tittelTekst': return 'Skjemaoversikt';
160+
case 'sjekkboksTekst': return 'Jeg godtar vilkårene';
161+
case 'modalTittel': return 'Bekreft valg';
162+
case 'chipTekst': return 'Filter: Aktive';
163+
case 'kopieringsTekst': return 'https://nav.no/skjema';
164+
case 'lesMerTittel': return 'Mer informasjon';
165+
case 'feltNavn': return 'epost';
166+
167+
// Values
168+
case 'valgtVerdi': return 'deltid';
169+
case 'valgtTekst': return 'Deltidsjobb';
170+
case 'søkeTekst': return 'stønad';
171+
case 'søkeResultater': return '42';
172+
case 'datoVerdi': return '2024-06-15';
173+
case 'datoFelt': return 'startDato';
174+
case 'harVerdi': return 'true';
175+
case 'filNavn': return 'vedlegg.pdf';
176+
case 'filStørrelse': return '1024';
177+
case 'filType': return 'application/pdf';
178+
case 'sjekkboksVerdi': return 'true';
179+
180+
// Types and variants
181+
case 'alertType': return 'warning';
182+
case 'alertVariant': return 'info';
183+
case 'knappType': return 'submit';
184+
case 'knappVariant': return 'primary';
185+
case 'chipVerdi': return 'aktiv';
186+
187+
// Navigation
188+
case 'fraFane': return 'oversikt';
189+
case 'tilFane': return 'detaljer';
190+
case 'faneTekst': return 'Skjemadetaljer';
191+
192+
// Methods
193+
case 'lukkMetode': return 'escape';
194+
195+
default: return 'eksempel-verdi';
196+
}
197+
};
198+
199+
// Update the ComponentAccordion component
200+
const ComponentAccordion = ({ component }: { component: { component: string; events: Array<{ name: string; details: Record<string, string | undefined> }> } }) => (
201+
<Accordion>
202+
<Accordion.Item>
203+
<Accordion.Header>
204+
<div className="flex justify-between items-center w-full py-2">
205+
<span className="font-medium">{component.component}</span>
206+
<Tag variant="neutral" size="xsmall" className="ml-2 mr-8">{component.events.length} hendelse{component.events.length !== 1 ? 'r' : ''}</Tag>
207+
</div>
208+
</Accordion.Header>
209+
<Accordion.Content>
210+
{component.events.map((event, eventIndex) => (
211+
<div key={event.name} className={eventIndex > 0 ? 'mt-8' : ''}>
212+
<Tag variant="info"className="mt-2" size="medium">{event.name}</Tag>
213+
<Table className="mt-6" size="small">
214+
<Table.Header>
215+
<Table.Row>
216+
<Table.HeaderCell>Detalj</Table.HeaderCell>
217+
<Table.HeaderCell>Forklaring</Table.HeaderCell>
218+
<Table.HeaderCell>Eksempel</Table.HeaderCell>
219+
</Table.Row>
220+
</Table.Header>
221+
<Table.Body>
222+
{Object.entries(event.details).map(([key]) => (
223+
<Table.Row key={key}>
224+
<Table.DataCell>
225+
<code>{key}</code>
226+
</Table.DataCell>
227+
<Table.DataCell>
228+
{key === 'knappTekst' ? 'Teksten på knappen' :
229+
key === 'komponentId' ? 'Unik ID for komponenten' :
230+
key === 'tittelTekst' ? 'Tekst i overskriften' :
231+
key === 'valgtVerdi' ? 'Verdien som ble valgt' :
232+
key === 'modalId' ? 'ID for modal-dialogen' :
233+
key === 'søkeTekst' ? 'Teksten som ble søkt etter' :
234+
key === 'søkeResultater' ? 'Antall søketreff' :
235+
key === 'filNavn' ? 'Navnet på filen' :
236+
key === 'filStørrelse' ? 'Filstørrelse i KB' :
237+
key === 'filType' ? 'Type fil (MIME)' :
238+
key === 'harVerdi' ? 'Om feltet har verdi' :
239+
key === 'lukkMetode' ? 'Hvordan dialogen ble lukket' :
240+
key === 'faneTekst' ? 'Tekst på fanen' :
241+
key === 'alertType' ? 'Type varsel' :
242+
key === 'feltNavn' ? 'Navn på feltet' :
243+
key === 'knappVariant' ? 'Variant av knappen (primær/sekundær)' :
244+
'Identifikator for komponenten'}
245+
</Table.DataCell>
246+
<Table.DataCell>
247+
<code>{getExampleValue(key, component.component)}</code>
248+
</Table.DataCell>
249+
</Table.Row>
250+
))}
251+
</Table.Body>
252+
</Table>
253+
</div>
254+
))}
255+
</Accordion.Content>
256+
</Accordion.Item>
257+
</Accordion>
258+
);
259+
260+
const AkselComponentEvents = () => {
261+
const [searchTerm, setSearchTerm] = useState('');
262+
263+
const filteredComponents = useMemo(() => {
264+
if (!searchTerm.trim()) {
265+
return akselComponentEvents;
266+
}
267+
268+
return akselComponentEvents.filter(component =>
269+
component.component.toLowerCase().includes(searchTerm.toLowerCase()) ||
270+
component.events.some(event =>
271+
event.name.toLowerCase().includes(searchTerm.toLowerCase())
272+
)
273+
);
274+
}, [searchTerm]);
275+
276+
// Group components by first letter for better organization
277+
const groupedComponents = useMemo(() => {
278+
const groups: Record<string, typeof akselComponentEvents> = {};
279+
280+
filteredComponents.forEach(component => {
281+
const firstLetter = component.component[0].toUpperCase();
282+
if (!groups[firstLetter]) {
283+
groups[firstLetter] = [];
284+
}
285+
groups[firstLetter].push(component);
286+
});
287+
288+
return Object.entries(groups).sort(([a], [b]) => a.localeCompare(b));
289+
}, [filteredComponents]);
290+
291+
// Fix search functionality
292+
const handleSearchChange = (value: string) => {
293+
setSearchTerm(value);
294+
};
295+
296+
return (
297+
<section id="aksel-komponenter">
298+
<Heading level="2" size="medium" spacing>
299+
Hendelser og detaljer for Aksel-komponenter
300+
</Heading>
301+
<p className="mb-6">
302+
For å sikre konsistent sporing på tvers av team når du bruker Aksel-komponenter,
303+
anbefaler vi følgende hendelsesnavn og detaljer. Dette gjør det lettere å analysere
304+
hvordan komponentene brukes og fungerer.
305+
</p>
306+
307+
<Box
308+
padding="6"
309+
borderRadius="medium"
310+
background="surface-subtle"
311+
className="mb-8"
312+
>
313+
<Label htmlFor="component-search" spacing>Søk etter komponenter eller hendelsesnavn</Label>
314+
<Search
315+
id="component-search"
316+
label="Søk"
317+
size="medium"
318+
variant="simple"
319+
placeholder="F.eks. Button, Modal, accordion åpnet..."
320+
onChange={handleSearchChange}
321+
onClear={() => setSearchTerm("")}
322+
value={searchTerm}
323+
/>
324+
325+
{searchTerm && (
326+
<div className="mt-4 text-sm text-text-subtle">
327+
{filteredComponents.length === 0 ?
328+
'Ingen komponenter funnet' :
329+
`Fant ${filteredComponents.length} ${filteredComponents.length === 1 ? 'komponent' : 'komponenter'}`
330+
}
331+
</div>
332+
)}
333+
</Box>
334+
335+
{/* Simplified structure with just one tab */}
336+
<Box
337+
padding="6"
338+
borderWidth="1"
339+
borderRadius="medium"
340+
borderColor="border-subtle"
341+
className="mt-6 mb-8"
342+
>
343+
<div className="flex items-center mb-4 pb-3 border-b border-border-subtle">
344+
<div className="flex items-center gap-3">
345+
<Heading level="3" size="small" className="m-0">Alle komponenter</Heading>
346+
<Tag variant="neutral" size="xsmall">{filteredComponents.length}</Tag>
347+
</div>
348+
</div>
349+
350+
{filteredComponents.length === 0 ? (
351+
<Box padding="6" background="surface-subtle" borderRadius="medium" className="text-center">
352+
<p>Ingen komponenter funnet som samsvarer med søket ditt.</p>
353+
</Box>
354+
) : (
355+
<div>
356+
{searchTerm.trim() ? (
357+
<VStack gap="0">
358+
{filteredComponents.map((component) => (
359+
<ComponentAccordion key={component.component} component={component} />
360+
))}
361+
</VStack>
362+
) : (
363+
<div>
364+
{groupedComponents.map(([letter, components]) => (
365+
<div key={letter} className="mb-8">
366+
<div className="flex items-center mb-4">
367+
<div className="w-10 h-10 flex items-center justify-center rounded-full bg-surface-action-subtle text-text-action">
368+
<span className="text-xl font-medium">{letter}</span>
369+
</div>
370+
<div className="h-px bg-border-subtle flex-grow ml-4"></div>
371+
</div>
372+
373+
<VStack gap="0" className="ml-2">
374+
{components.map((component) => (
375+
<ComponentAccordion key={component.component} component={component} />
376+
))}
377+
</VStack>
378+
</div>
379+
))}
380+
</div>
381+
)}
382+
</div>
383+
)}
384+
</Box>
385+
</section>
386+
);
387+
};
388+
389+
export default AkselComponentEvents;

0 commit comments

Comments
 (0)