@ndla/ui 47.2.1 → 47.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/DefinitionList/DefinitionDescription.js +2 -2
- package/es/DefinitionList/DefinitionTerm.js +2 -2
- package/es/LinkBlock/LinkBlock.js +6 -6
- package/es/Messages/MessageBox.js +22 -56
- package/lib/DefinitionList/DefinitionDescription.js +1 -1
- package/lib/DefinitionList/DefinitionTerm.js +1 -1
- package/lib/LinkBlock/LinkBlock.js +6 -6
- package/lib/Messages/MessageBox.d.ts +2 -2
- package/lib/Messages/MessageBox.js +21 -55
- package/package.json +3 -3
- package/src/DefinitionList/DefinitionDescription.tsx +1 -2
- package/src/DefinitionList/DefinitionTerm.tsx +1 -2
- package/src/Embed/FootnoteEmbed.stories.tsx +134 -0
- package/src/LinkBlock/LinkBlock.tsx +1 -1
- package/src/Messages/MessageBox.stories.tsx +40 -3
- package/src/Messages/MessageBox.tsx +28 -62
- package/src/ResourceGroup/ResourceItem.stories.tsx +103 -0
- package/src/TreeStructure/TreeStructure.stories.tsx +271 -0
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { useTranslation } from 'react-i18next';
|
|
10
10
|
import { Meta, StoryFn } from '@storybook/react';
|
|
11
|
+
import { Alarm, HumanMaleBoard, InformationOutline, WarningOutline } from '@ndla/icons/common';
|
|
11
12
|
import MessageBox from './MessageBox';
|
|
12
13
|
import { defaultParameters } from '../../../../stories/defaults';
|
|
13
14
|
|
|
@@ -26,12 +27,22 @@ export default {
|
|
|
26
27
|
|
|
27
28
|
export const Default: StoryFn<typeof MessageBox> = ({ ...args }) => {
|
|
28
29
|
const { t } = useTranslation();
|
|
29
|
-
return
|
|
30
|
+
return (
|
|
31
|
+
<MessageBox {...args}>
|
|
32
|
+
<InformationOutline />
|
|
33
|
+
{t('messageBoxInfo.noContent')}
|
|
34
|
+
</MessageBox>
|
|
35
|
+
);
|
|
30
36
|
};
|
|
31
37
|
|
|
32
38
|
export const WithoutCloseButton: StoryFn<typeof MessageBox> = () => {
|
|
33
39
|
const { t } = useTranslation();
|
|
34
|
-
return
|
|
40
|
+
return (
|
|
41
|
+
<MessageBox>
|
|
42
|
+
<InformationOutline />
|
|
43
|
+
{t('messageBoxInfo.subjectOutdated')}
|
|
44
|
+
</MessageBox>
|
|
45
|
+
);
|
|
35
46
|
};
|
|
36
47
|
|
|
37
48
|
export const WithLinks: StoryFn<typeof MessageBox> = () => {
|
|
@@ -44,6 +55,7 @@ export const WithLinks: StoryFn<typeof MessageBox> = () => {
|
|
|
44
55
|
{ text: 'link 3', href: '#' },
|
|
45
56
|
]}
|
|
46
57
|
>
|
|
58
|
+
<InformationOutline />
|
|
47
59
|
{t('messageBoxInfo.newVersion')}
|
|
48
60
|
</MessageBox>
|
|
49
61
|
);
|
|
@@ -51,10 +63,35 @@ export const WithLinks: StoryFn<typeof MessageBox> = () => {
|
|
|
51
63
|
|
|
52
64
|
export const Ghost: StoryFn<typeof MessageBox> = () => {
|
|
53
65
|
const { t } = useTranslation();
|
|
54
|
-
return
|
|
66
|
+
return (
|
|
67
|
+
<MessageBox type="ghost">
|
|
68
|
+
<HumanMaleBoard />
|
|
69
|
+
{t('messageBoxInfo.feide')}
|
|
70
|
+
</MessageBox>
|
|
71
|
+
);
|
|
55
72
|
};
|
|
56
73
|
|
|
57
74
|
export const Danger: StoryFn<typeof MessageBox> = () => {
|
|
75
|
+
const { t } = useTranslation();
|
|
76
|
+
return (
|
|
77
|
+
<MessageBox type="danger">
|
|
78
|
+
<WarningOutline />
|
|
79
|
+
{t('messageBoxInfo.feide')}
|
|
80
|
+
</MessageBox>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const WithCustomIcon: StoryFn<typeof MessageBox> = () => {
|
|
85
|
+
const { t } = useTranslation();
|
|
86
|
+
return (
|
|
87
|
+
<MessageBox type="danger">
|
|
88
|
+
<Alarm />
|
|
89
|
+
{t('messageBoxInfo.feide')}
|
|
90
|
+
</MessageBox>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const WithoutIcon: StoryFn<typeof MessageBox> = () => {
|
|
58
95
|
const { t } = useTranslation();
|
|
59
96
|
return <MessageBox type="danger">{t('messageBoxInfo.feide')}</MessageBox>;
|
|
60
97
|
};
|
|
@@ -8,19 +8,27 @@
|
|
|
8
8
|
|
|
9
9
|
import styled from '@emotion/styled';
|
|
10
10
|
import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
|
|
11
|
-
import {
|
|
12
|
-
|
|
11
|
+
import { Forward } from '@ndla/icons/common';
|
|
13
12
|
import { CloseButton } from '@ndla/button';
|
|
14
13
|
import { css } from '@emotion/react';
|
|
15
14
|
import { ReactNode } from 'react';
|
|
16
15
|
|
|
17
16
|
type MessageBoxType = 'ghost' | 'danger';
|
|
18
17
|
|
|
19
|
-
interface
|
|
18
|
+
interface LinkProps {
|
|
19
|
+
href?: string;
|
|
20
|
+
text?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface MessageBoxProps {
|
|
20
24
|
type?: MessageBoxType;
|
|
25
|
+
children?: ReactNode;
|
|
26
|
+
links?: LinkProps[];
|
|
27
|
+
showCloseButton?: boolean;
|
|
28
|
+
onClose?: () => void;
|
|
21
29
|
}
|
|
22
30
|
|
|
23
|
-
const MessageBoxWrapper = styled.div
|
|
31
|
+
const MessageBoxWrapper = styled.div`
|
|
24
32
|
display: flex;
|
|
25
33
|
padding: ${spacing.small};
|
|
26
34
|
font-family: ${fonts.sans};
|
|
@@ -32,21 +40,14 @@ const MessageBoxWrapper = styled.div<StyledProps>`
|
|
|
32
40
|
${mq.range({ until: breakpoints.tabletWide })} {
|
|
33
41
|
${fonts.sizes('16px')};
|
|
34
42
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
${({ type }) =>
|
|
45
|
-
type === 'danger' &&
|
|
46
|
-
css`
|
|
47
|
-
background: ${colors.support.redLightest};
|
|
48
|
-
color: ${colors.text.primary};
|
|
49
|
-
`}
|
|
43
|
+
&[data-type='ghost'] {
|
|
44
|
+
background: transparent;
|
|
45
|
+
color: ${colors.brand.greyDark};
|
|
46
|
+
}
|
|
47
|
+
&[data-type='danger'] {
|
|
48
|
+
background: ${colors.support.redLightest};
|
|
49
|
+
color: ${colors.text.primary};
|
|
50
|
+
}
|
|
50
51
|
`;
|
|
51
52
|
|
|
52
53
|
const InfoWrapper = styled.div`
|
|
@@ -54,23 +55,14 @@ const InfoWrapper = styled.div`
|
|
|
54
55
|
flex-direction: row;
|
|
55
56
|
flex: 1;
|
|
56
57
|
padding: ${spacing.small};
|
|
57
|
-
padding-right: 0;
|
|
58
58
|
`;
|
|
59
59
|
|
|
60
|
-
const
|
|
61
|
-
& p {
|
|
62
|
-
margin: 0;
|
|
63
|
-
}
|
|
64
|
-
`;
|
|
65
|
-
|
|
66
|
-
const IconWrapper = styled.div`
|
|
60
|
+
const ChildrenWrapper = styled.div`
|
|
67
61
|
display: flex;
|
|
68
|
-
|
|
69
|
-
padding-right: ${spacing.small};
|
|
70
|
-
|
|
62
|
+
gap: ${spacing.small};
|
|
71
63
|
svg {
|
|
72
|
-
width: 24px;
|
|
73
|
-
height: 24px;
|
|
64
|
+
min-width: 24px;
|
|
65
|
+
min-height: 24px;
|
|
74
66
|
}
|
|
75
67
|
`;
|
|
76
68
|
|
|
@@ -79,7 +71,7 @@ const LinkWrapper = styled.div`
|
|
|
79
71
|
flex-wrap: wrap;
|
|
80
72
|
gap: ${spacing.normal};
|
|
81
73
|
padding-top: ${spacing.nsmall};
|
|
82
|
-
|
|
74
|
+
padding-left: ${spacing.mediumlarge};
|
|
83
75
|
svg {
|
|
84
76
|
flex-shrink: 0;
|
|
85
77
|
}
|
|
@@ -97,38 +89,12 @@ const StyledClosebutton = styled(CloseButton)`
|
|
|
97
89
|
padding: 0;
|
|
98
90
|
`;
|
|
99
91
|
|
|
100
|
-
|
|
101
|
-
href?: string;
|
|
102
|
-
text?: string;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
interface Props {
|
|
106
|
-
type?: MessageBoxType;
|
|
107
|
-
children?: ReactNode;
|
|
108
|
-
links?: LinkProps[];
|
|
109
|
-
showCloseButton?: boolean;
|
|
110
|
-
onClose?: () => void;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const Icon = ({ type }: StyledProps) => {
|
|
114
|
-
if (type === 'ghost') {
|
|
115
|
-
return <HumanMaleBoard />;
|
|
116
|
-
}
|
|
117
|
-
if (type === 'danger') {
|
|
118
|
-
return <WarningOutline />;
|
|
119
|
-
}
|
|
120
|
-
return <InformationOutline />;
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
export const MessageBox = ({ type, children = '', links, showCloseButton, onClose }: Props) => {
|
|
92
|
+
export const MessageBox = ({ type, children, links, showCloseButton, onClose }: MessageBoxProps) => {
|
|
124
93
|
return (
|
|
125
|
-
<MessageBoxWrapper type={type}>
|
|
94
|
+
<MessageBoxWrapper data-type={type}>
|
|
126
95
|
<InfoWrapper>
|
|
127
|
-
<IconWrapper>
|
|
128
|
-
<Icon type={type} />
|
|
129
|
-
</IconWrapper>
|
|
130
96
|
<div>
|
|
131
|
-
<
|
|
97
|
+
<ChildrenWrapper>{children}</ChildrenWrapper>
|
|
132
98
|
{links && (
|
|
133
99
|
<LinkWrapper>
|
|
134
100
|
{links.map((x) => (
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2023-present, NDLA.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the GPLv3 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
10
|
+
import { FavoriteButton } from '@ndla/button';
|
|
11
|
+
import ResourceItem from './ResourceItem';
|
|
12
|
+
import { defaultParameters } from '../../../../stories/defaults';
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
title: 'Components/ResourceItem',
|
|
16
|
+
tags: ['autodocs'],
|
|
17
|
+
component: ResourceItem,
|
|
18
|
+
parameters: {
|
|
19
|
+
inlineStories: true,
|
|
20
|
+
...defaultParameters,
|
|
21
|
+
},
|
|
22
|
+
args: {
|
|
23
|
+
id: 'urn:resource:a7a49c0a-32ea-4343-8b11-bd6d65c24f87',
|
|
24
|
+
name: 'Refleksjonsoppgave om ideer og idéutvikling',
|
|
25
|
+
path: '',
|
|
26
|
+
contentType: 'subject-material',
|
|
27
|
+
additional: false,
|
|
28
|
+
heartButton: () => <FavoriteButton />,
|
|
29
|
+
access: undefined,
|
|
30
|
+
},
|
|
31
|
+
} as Meta<typeof ResourceItem>;
|
|
32
|
+
|
|
33
|
+
export const Default: StoryObj<typeof ResourceItem> = {};
|
|
34
|
+
|
|
35
|
+
export const WithCoreOrAdditionalIndicator: StoryObj<typeof ResourceItem> = {
|
|
36
|
+
args: {
|
|
37
|
+
contentTypeName: 'Fagstoff',
|
|
38
|
+
contentTypeDescription: 'Kjernestoff',
|
|
39
|
+
showAdditionalResources: true,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const WithCoreOrAdditionalIndicatorAdditional: StoryObj<typeof ResourceItem> = {
|
|
44
|
+
args: {
|
|
45
|
+
additional: true,
|
|
46
|
+
contentTypeName: 'Fagstoff',
|
|
47
|
+
contentTypeDescription: 'Tilleggsstoff',
|
|
48
|
+
showAdditionalResources: true,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const RelevanceIndicatorWithoutLabel: StoryObj<typeof ResourceItem> = {
|
|
53
|
+
args: {
|
|
54
|
+
contentTypeDescription: 'Kjernestoff',
|
|
55
|
+
showAdditionalResources: true,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const OnlyAvailableForTeachers: StoryObj<typeof ResourceItem> = {
|
|
60
|
+
args: {
|
|
61
|
+
access: 'teacher',
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const CurrentPage: StoryObj<typeof ResourceItem> = {
|
|
66
|
+
args: { active: true },
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const SubjectMaterial: StoryObj<typeof ResourceItem> = {
|
|
70
|
+
args: {
|
|
71
|
+
contentType: 'subject-material',
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const TasksAndActivities: StoryObj<typeof ResourceItem> = {
|
|
76
|
+
args: {
|
|
77
|
+
contentType: 'tasks-and-activities',
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const AssessmentResource: StoryObj<typeof ResourceItem> = {
|
|
82
|
+
args: {
|
|
83
|
+
contentType: 'assessment-resources',
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const ExternalLearningResource: StoryObj<typeof ResourceItem> = {
|
|
88
|
+
args: {
|
|
89
|
+
contentType: 'external-learning-resources',
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const SourceMaterial: StoryObj<typeof ResourceItem> = {
|
|
94
|
+
args: {
|
|
95
|
+
contentType: 'source-material',
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const LearningPath: StoryObj<typeof ResourceItem> = {
|
|
100
|
+
args: {
|
|
101
|
+
contentType: 'learning-path',
|
|
102
|
+
},
|
|
103
|
+
};
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2023-present, NDLA.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the GPLv3 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
|
|
10
|
+
import { Meta, StoryFn } from '@storybook/react';
|
|
11
|
+
import styled from '@emotion/styled';
|
|
12
|
+
import { IFolder } from '@ndla/types-backend/learningpath-api';
|
|
13
|
+
import { colors, spacing } from '@ndla/core';
|
|
14
|
+
import { uuid } from '@ndla/util';
|
|
15
|
+
import TreeStructure, { TreeStructureProps } from './TreeStructure';
|
|
16
|
+
import { defaultParameters } from '../../../../stories/defaults';
|
|
17
|
+
import { flattenFolders } from './helperFunctions';
|
|
18
|
+
import { FolderInput } from '../MyNdla';
|
|
19
|
+
|
|
20
|
+
const MY_FOLDERS_ID = 'folders';
|
|
21
|
+
|
|
22
|
+
const Container = styled.div`
|
|
23
|
+
display: flex;
|
|
24
|
+
margin-top: 40px;
|
|
25
|
+
max-width: 600px;
|
|
26
|
+
&[data-type='picker'] {
|
|
27
|
+
height: 250px;
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
const StyledFolderInput = styled(FolderInput)`
|
|
32
|
+
border-left: ${spacing.xsmall} solid ${colors.brand.light};
|
|
33
|
+
border-right: ${spacing.xsmall} solid ${colors.brand.light};
|
|
34
|
+
&:focus-within {
|
|
35
|
+
border-color: ${colors.brand.light};
|
|
36
|
+
}
|
|
37
|
+
/* Not good practice, but necessary to give error message same padding as caused by border. */
|
|
38
|
+
& + span {
|
|
39
|
+
padding: 0 ${spacing.xsmall};
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
const targetResource: TreeStructureProps['targetResource'] = {
|
|
44
|
+
id: 'test-resource',
|
|
45
|
+
resourceId: '123',
|
|
46
|
+
resourceType: 'type',
|
|
47
|
+
tags: [],
|
|
48
|
+
path: '',
|
|
49
|
+
created: '',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const STRUCTURE_EXAMPLE: IFolder[] = [
|
|
53
|
+
{
|
|
54
|
+
id: '1',
|
|
55
|
+
name: 'Mine favoritter',
|
|
56
|
+
status: 'private',
|
|
57
|
+
breadcrumbs: [{ id: '1', name: 'Mine Favoritter' }],
|
|
58
|
+
resources: [targetResource],
|
|
59
|
+
created: '2023-03-03T08:40:23.444Z',
|
|
60
|
+
updated: '2023-03-03T08:40:23.444Z',
|
|
61
|
+
subfolders: [
|
|
62
|
+
{
|
|
63
|
+
id: '2',
|
|
64
|
+
name: 'Eksamen',
|
|
65
|
+
status: 'shared',
|
|
66
|
+
breadcrumbs: [
|
|
67
|
+
{ id: '1', name: 'Mine Favoritter' },
|
|
68
|
+
{ id: '2', name: 'Eksamen' },
|
|
69
|
+
],
|
|
70
|
+
created: '2023-03-03T08:40:23.444Z',
|
|
71
|
+
updated: '2023-03-03T08:40:23.444Z',
|
|
72
|
+
resources: [],
|
|
73
|
+
subfolders: [
|
|
74
|
+
{
|
|
75
|
+
id: '3',
|
|
76
|
+
name: 'Eksamens oppgaver',
|
|
77
|
+
status: 'shared',
|
|
78
|
+
breadcrumbs: [
|
|
79
|
+
{ id: '1', name: 'Mine Favoritter' },
|
|
80
|
+
{ id: '2', name: 'Eksamen' },
|
|
81
|
+
{ id: '3', name: 'Eksamens oppgaver' },
|
|
82
|
+
],
|
|
83
|
+
resources: [],
|
|
84
|
+
subfolders: [],
|
|
85
|
+
created: '2023-03-03T08:40:23.444Z',
|
|
86
|
+
updated: '2023-03-03T08:40:23.444Z',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: '4',
|
|
90
|
+
name: 'Eksamen 2022',
|
|
91
|
+
status: 'private',
|
|
92
|
+
breadcrumbs: [
|
|
93
|
+
{ id: '1', name: 'Mine Favoritter' },
|
|
94
|
+
{ id: '2', name: 'Eksamen' },
|
|
95
|
+
{ id: '4', name: 'Eksamen 2022' },
|
|
96
|
+
],
|
|
97
|
+
resources: [],
|
|
98
|
+
subfolders: [],
|
|
99
|
+
created: '2023-03-03T08:40:23.444Z',
|
|
100
|
+
updated: '2023-03-03T08:40:23.444Z',
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: '5',
|
|
106
|
+
name: 'Oppgaver',
|
|
107
|
+
status: 'shared',
|
|
108
|
+
breadcrumbs: [
|
|
109
|
+
{ id: '1', name: 'Mine Favoritter' },
|
|
110
|
+
{ id: '5', name: 'Oppgaver' },
|
|
111
|
+
],
|
|
112
|
+
resources: [],
|
|
113
|
+
subfolders: [],
|
|
114
|
+
created: '2023-03-03T08:40:23.444Z',
|
|
115
|
+
updated: '2023-03-03T08:40:23.444Z',
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
const FOLDER_TREE_STRUCTURE: IFolder[] = [
|
|
122
|
+
{
|
|
123
|
+
id: MY_FOLDERS_ID,
|
|
124
|
+
name: 'Mine mapper',
|
|
125
|
+
status: 'private',
|
|
126
|
+
breadcrumbs: [],
|
|
127
|
+
resources: [],
|
|
128
|
+
subfolders: [...STRUCTURE_EXAMPLE],
|
|
129
|
+
created: '2023-03-03T08:40:23.444Z',
|
|
130
|
+
updated: '2023-03-03T08:40:23.444Z',
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
export default {
|
|
135
|
+
title: 'Components/TreeStructure',
|
|
136
|
+
tags: ['autodocs'],
|
|
137
|
+
component: TreeStructure,
|
|
138
|
+
parameters: {
|
|
139
|
+
inlineStories: true,
|
|
140
|
+
...defaultParameters,
|
|
141
|
+
},
|
|
142
|
+
args: {
|
|
143
|
+
defaultOpenFolders: [MY_FOLDERS_ID],
|
|
144
|
+
targetResource: targetResource,
|
|
145
|
+
label: 'Velg mappe',
|
|
146
|
+
maxLevel: 5,
|
|
147
|
+
type: 'picker',
|
|
148
|
+
// eslint-disable-next-line no-console
|
|
149
|
+
onSelectFolder: console.log,
|
|
150
|
+
},
|
|
151
|
+
argTypes: {
|
|
152
|
+
folders: { control: false },
|
|
153
|
+
},
|
|
154
|
+
} as Meta<typeof TreeStructure>;
|
|
155
|
+
|
|
156
|
+
export const Default: StoryFn<typeof TreeStructure> = ({ ...args }) => {
|
|
157
|
+
const [structure, setStructure] = useState<IFolder[]>(
|
|
158
|
+
args.type === 'picker' ? FOLDER_TREE_STRUCTURE : STRUCTURE_EXAMPLE,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
setStructure(args.type === 'picker' ? FOLDER_TREE_STRUCTURE : STRUCTURE_EXAMPLE);
|
|
163
|
+
}, [args.type]);
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<Container data-type={args.type}>
|
|
167
|
+
<TreeStructure
|
|
168
|
+
{...args}
|
|
169
|
+
folders={structure}
|
|
170
|
+
newFolderInput={({ parentId, onClose, onCreate }) => (
|
|
171
|
+
<NewFolder
|
|
172
|
+
structure={structure}
|
|
173
|
+
setStructure={setStructure}
|
|
174
|
+
parentId={parentId}
|
|
175
|
+
onClose={onClose}
|
|
176
|
+
onCreate={onCreate}
|
|
177
|
+
/>
|
|
178
|
+
)}
|
|
179
|
+
/>
|
|
180
|
+
</Container>
|
|
181
|
+
);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
interface NewFolderProps {
|
|
185
|
+
parentId: string;
|
|
186
|
+
structure: IFolder[];
|
|
187
|
+
setStructure: Dispatch<SetStateAction<IFolder[]>>;
|
|
188
|
+
onClose?: () => void;
|
|
189
|
+
onCreate?: (folder: IFolder, parentId: string) => void;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const generateNewFolder = (name: string, id: string, breadcrumbs: { id: string; name: string }[]): IFolder => ({
|
|
193
|
+
id,
|
|
194
|
+
name,
|
|
195
|
+
status: 'private',
|
|
196
|
+
subfolders: [],
|
|
197
|
+
breadcrumbs: breadcrumbs.concat({ name, id }),
|
|
198
|
+
resources: [],
|
|
199
|
+
created: '2023-03-03T08:40:23.444Z',
|
|
200
|
+
updated: '2023-03-03T08:40:23.444Z',
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const NewFolder = ({ parentId, onClose, structure, setStructure, onCreate }: NewFolderProps) => {
|
|
204
|
+
const [name, setName] = useState('');
|
|
205
|
+
const [loading, setLoading] = useState(false);
|
|
206
|
+
const [error, setError] = useState('');
|
|
207
|
+
|
|
208
|
+
const onSave = async () => {
|
|
209
|
+
if (error) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (name === '') {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
setLoading(true);
|
|
216
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
217
|
+
setLoading(false);
|
|
218
|
+
const flattenedStructure = flattenFolders(structure);
|
|
219
|
+
const targetFolder = flattenedStructure.find((folder) => folder.id === parentId);
|
|
220
|
+
const newFolderId = uuid();
|
|
221
|
+
const newFolder = generateNewFolder(name, newFolderId, targetFolder?.breadcrumbs ?? []);
|
|
222
|
+
if (targetFolder) {
|
|
223
|
+
setStructure((oldStructure) => {
|
|
224
|
+
targetFolder.subfolders.unshift(newFolder);
|
|
225
|
+
return oldStructure;
|
|
226
|
+
});
|
|
227
|
+
} else {
|
|
228
|
+
setStructure((old) => [newFolder].concat(old));
|
|
229
|
+
}
|
|
230
|
+
onCreate?.(newFolder, parentId);
|
|
231
|
+
onClose?.();
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
useEffect(() => {
|
|
235
|
+
if (name.length === 0) {
|
|
236
|
+
setError('Navn er påkrevd');
|
|
237
|
+
} else {
|
|
238
|
+
setError('');
|
|
239
|
+
}
|
|
240
|
+
}, [name]);
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<StyledFolderInput
|
|
244
|
+
// eslint-disable-next-line jsx-a11y/no-autofocus
|
|
245
|
+
autoFocus
|
|
246
|
+
labelHidden
|
|
247
|
+
name="name"
|
|
248
|
+
label={'Mine mapper'}
|
|
249
|
+
placeholder={'Skriv inn mappenavn'}
|
|
250
|
+
loading={loading}
|
|
251
|
+
onClose={onClose}
|
|
252
|
+
onSave={onSave}
|
|
253
|
+
error={error}
|
|
254
|
+
value={name}
|
|
255
|
+
onChange={(e) => {
|
|
256
|
+
if (!loading) {
|
|
257
|
+
setName(e.currentTarget.value);
|
|
258
|
+
}
|
|
259
|
+
}}
|
|
260
|
+
onKeyDown={(e) => {
|
|
261
|
+
if (e.key === 'Escape') {
|
|
262
|
+
e.preventDefault();
|
|
263
|
+
onClose?.();
|
|
264
|
+
} else if (e.key === 'Enter') {
|
|
265
|
+
e.preventDefault();
|
|
266
|
+
onSave();
|
|
267
|
+
}
|
|
268
|
+
}}
|
|
269
|
+
/>
|
|
270
|
+
);
|
|
271
|
+
};
|