@ndla/ui 56.0.14-alpha.0 → 56.0.16-alpha.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.
Files changed (73) hide show
  1. package/dist/panda.buildinfo.json +24 -21
  2. package/dist/styles.css +82 -67
  3. package/es/Article/Article.js +22 -94
  4. package/es/Article/index.js +1 -1
  5. package/es/CampaignBlock/CampaignBlock.js +33 -5
  6. package/es/ContentTypeBadge/ContentTypeBadge.js +2 -0
  7. package/es/TreeStructure/TreeStructure.js +292 -181
  8. package/es/TreeStructure/helperFunctions.js +0 -3
  9. package/es/TreeStructure/index.js +1 -2
  10. package/es/index.js +1 -1
  11. package/es/locale/messages-en.js +3 -3
  12. package/es/locale/messages-nb.js +3 -3
  13. package/es/locale/messages-nn.js +3 -3
  14. package/es/locale/messages-se.js +3 -3
  15. package/es/locale/messages-sma.js +3 -3
  16. package/es/styles.css +82 -67
  17. package/lib/Article/Article.d.ts +3 -18
  18. package/lib/Article/Article.js +22 -94
  19. package/lib/Article/index.d.ts +1 -1
  20. package/lib/Article/index.js +0 -12
  21. package/lib/CampaignBlock/CampaignBlock.js +33 -5
  22. package/lib/ContentTypeBadge/ContentTypeBadge.js +2 -0
  23. package/lib/TreeStructure/TreeStructure.d.ts +7 -6
  24. package/lib/TreeStructure/TreeStructure.js +293 -180
  25. package/lib/TreeStructure/helperFunctions.d.ts +0 -2
  26. package/lib/TreeStructure/helperFunctions.js +2 -6
  27. package/lib/TreeStructure/index.d.ts +1 -2
  28. package/lib/TreeStructure/index.js +2 -3
  29. package/lib/TreeStructure/types.d.ts +4 -22
  30. package/lib/index.d.ts +1 -1
  31. package/lib/index.js +0 -12
  32. package/lib/locale/messages-en.js +3 -3
  33. package/lib/locale/messages-nb.js +3 -3
  34. package/lib/locale/messages-nn.js +3 -3
  35. package/lib/locale/messages-se.js +3 -3
  36. package/lib/locale/messages-sma.js +3 -3
  37. package/lib/styles.css +82 -67
  38. package/package.json +7 -8
  39. package/src/Article/Article.tsx +22 -107
  40. package/src/Article/index.ts +0 -2
  41. package/src/CampaignBlock/CampaignBlock.tsx +35 -4
  42. package/src/ContentTypeBadge/ContentTypeBadge.tsx +2 -0
  43. package/src/TreeStructure/TreeStructure.stories.tsx +38 -68
  44. package/src/TreeStructure/TreeStructure.tsx +307 -194
  45. package/src/TreeStructure/helperFunctions.ts +0 -5
  46. package/src/TreeStructure/index.ts +1 -2
  47. package/src/TreeStructure/types.ts +4 -25
  48. package/src/index.ts +0 -2
  49. package/src/locale/messages-en.ts +3 -3
  50. package/src/locale/messages-nb.ts +3 -3
  51. package/src/locale/messages-nn.ts +3 -3
  52. package/src/locale/messages-se.ts +3 -3
  53. package/src/locale/messages-sma.ts +3 -3
  54. package/es/TreeStructure/AddFolderButton.js +0 -80
  55. package/es/TreeStructure/ComboboxButton.js +0 -127
  56. package/es/TreeStructure/FolderItem.js +0 -225
  57. package/es/TreeStructure/FolderItems.js +0 -95
  58. package/es/TreeStructure/arrowNavigation.js +0 -35
  59. package/lib/TreeStructure/AddFolderButton.d.ts +0 -17
  60. package/lib/TreeStructure/AddFolderButton.js +0 -85
  61. package/lib/TreeStructure/ComboboxButton.d.ts +0 -27
  62. package/lib/TreeStructure/ComboboxButton.js +0 -134
  63. package/lib/TreeStructure/FolderItem.d.ts +0 -17
  64. package/lib/TreeStructure/FolderItem.js +0 -230
  65. package/lib/TreeStructure/FolderItems.d.ts +0 -22
  66. package/lib/TreeStructure/FolderItems.js +0 -100
  67. package/lib/TreeStructure/arrowNavigation.d.ts +0 -10
  68. package/lib/TreeStructure/arrowNavigation.js +0 -42
  69. package/src/TreeStructure/AddFolderButton.tsx +0 -79
  70. package/src/TreeStructure/ComboboxButton.tsx +0 -172
  71. package/src/TreeStructure/FolderItem.tsx +0 -307
  72. package/src/TreeStructure/FolderItems.tsx +0 -121
  73. package/src/TreeStructure/arrowNavigation.ts +0 -54
@@ -10,78 +10,16 @@ import { ComponentPropsWithRef, ReactNode, forwardRef } from "react";
10
10
  import { ark, type HTMLArkProps } from "@ark-ui/react";
11
11
  import { Heading, Text } from "@ndla/primitives";
12
12
  import { cx } from "@ndla/styled-system/css";
13
- import { styled } from "@ndla/styled-system/jsx";
14
- import { JsxStyleProps, StyledVariantProps, SystemStyleObject } from "@ndla/styled-system/types";
13
+ import { Stack, styled } from "@ndla/styled-system/jsx";
14
+ import { JsxStyleProps } from "@ndla/styled-system/types";
15
15
  import { ArticleByline } from "./ArticleByline";
16
16
  import { ContentTypeBadgeNew } from "..";
17
17
  import { ContentType } from "../ContentTypeBadge/ContentTypeBadgeNew";
18
18
  import { Article as ArticleType } from "../types";
19
19
 
20
- const articlePadding: SystemStyleObject = {
21
- paddingInline: "8%",
22
- };
23
-
24
- const paddingBlockEnd: SystemStyleObject = {
25
- paddingBlockEnd: "xsmall",
26
- tablet: {
27
- paddingBlockEnd: "medium",
28
- },
29
- desktop: {
30
- paddingBlockEnd: "xxlarge",
31
- },
32
- };
33
-
34
- const paddingBlockStart: SystemStyleObject = {
35
- paddingBlockStart: "xsmall",
36
- tablet: {
37
- paddingBlockStart: "medium",
38
- },
39
- desktop: {
40
- paddingBlockStart: "xxlarge",
41
- },
42
- };
20
+ const StyledArticleContent = styled(ark.section, {}, { baseComponent: true });
43
21
 
44
- export const ArticlePadding = styled(
45
- ark.div,
46
- {
47
- base: {
48
- ...articlePadding,
49
- width: "100%",
50
- },
51
- variants: {
52
- padStart: {
53
- true: paddingBlockStart,
54
- },
55
- padEnd: {
56
- true: paddingBlockEnd,
57
- },
58
- },
59
- },
60
- { baseComponent: true },
61
- );
62
-
63
- const StyledArticleContent = styled(
64
- ark.section,
65
- {
66
- base: {
67
- background: "surface.default",
68
- },
69
- variants: {
70
- padded: {
71
- true: {
72
- ...articlePadding,
73
- ...paddingBlockStart,
74
- ...paddingBlockEnd,
75
- },
76
- },
77
- },
78
- },
79
- { baseComponent: true },
80
- );
81
-
82
- type ArticleContentVariantProps = StyledVariantProps<typeof StyledArticleContent>;
83
-
84
- export const ArticleContent = forwardRef<HTMLElement, HTMLArkProps<"div"> & JsxStyleProps & ArticleContentVariantProps>(
22
+ export const ArticleContent = forwardRef<HTMLElement, HTMLArkProps<"div"> & JsxStyleProps>(
85
23
  ({ className, ...props }, ref) => (
86
24
  <StyledArticleContent className={cx("ndla-article", className)} {...props} ref={ref} />
87
25
  ),
@@ -91,8 +29,10 @@ const StyledArticleWrapper = styled(
91
29
  ark.article,
92
30
  {
93
31
  base: {
32
+ background: "background.default",
94
33
  display: "flex",
95
34
  flexDirection: "column",
35
+ gap: "xxlarge",
96
36
  color: "text.default",
97
37
  alignItems: "center",
98
38
  width: "100%",
@@ -123,7 +63,6 @@ export const ArticleHGroup = styled(
123
63
  width: "100%",
124
64
  flexDirection: "column",
125
65
  alignItems: "flex-start",
126
- gap: "xsmall",
127
66
  "& h1": {
128
67
  overflowWrap: "anywhere",
129
68
  },
@@ -132,42 +71,16 @@ export const ArticleHGroup = styled(
132
71
  { baseComponent: true },
133
72
  );
134
73
 
135
- export const ArticleActionWrapper = styled(
136
- ark.div,
137
- {
138
- base: {
139
- position: "absolute",
140
- right: "8%",
141
- top: "xsmall",
142
- tablet: {
143
- top: "medium",
144
- },
145
- desktop: {
146
- top: "xxlarge",
147
- },
148
- },
149
- },
150
- { baseComponent: true },
151
- );
152
-
153
74
  export const ArticleHeader = styled(
154
75
  ark.header,
155
76
  {
156
77
  base: {
157
78
  display: "flex",
158
79
  flexDirection: "column",
159
- background: "surface.default",
160
80
  gap: "medium",
161
81
  alignItems: "flex-start",
162
82
  width: "100%",
163
- },
164
- variants: {
165
- padded: {
166
- true: {
167
- ...articlePadding,
168
- ...paddingBlockStart,
169
- },
170
- },
83
+ paddingBlockStart: "xxlarge",
171
84
  },
172
85
  },
173
86
  { baseComponent: true },
@@ -179,22 +92,22 @@ export const ArticleFooter = styled(
179
92
  base: {
180
93
  display: "flex",
181
94
  flexDirection: "column",
182
- background: "surface.default",
183
95
  gap: "xxlarge",
184
96
  width: "100%",
185
- },
186
- variants: {
187
- padded: {
188
- true: {
189
- ...articlePadding,
190
- ...paddingBlockEnd,
191
- },
97
+ "& > :is(:last-child)": {
98
+ paddingBlockEnd: "5xlarge",
192
99
  },
193
100
  },
194
101
  },
195
102
  { baseComponent: true },
196
103
  );
197
104
 
105
+ const StyledStack = styled(Stack, {
106
+ base: {
107
+ width: "100%",
108
+ },
109
+ });
110
+
198
111
  interface ArticleTitleProps {
199
112
  heartButton?: ReactNode;
200
113
  contentType?: ContentType;
@@ -215,10 +128,12 @@ export const ArticleTitle = ({
215
128
  competenceGoals,
216
129
  }: ArticleTitleProps) => {
217
130
  return (
218
- <ArticleHeader padded>
131
+ <ArticleHeader>
219
132
  <ArticleHGroup>
220
- {!!contentType && <ContentTypeBadgeNew contentType={contentType} />}
221
- {!!heartButton && <ArticleActionWrapper>{heartButton}</ArticleActionWrapper>}
133
+ <StyledStack justify="space-between" align="center" direction="row" gap="small">
134
+ {!!contentType && <ContentTypeBadgeNew contentType={contentType} />}
135
+ {heartButton}
136
+ </StyledStack>
222
137
  <Heading textStyle="heading.large" id={id} lang={lang}>
223
138
  {title}
224
139
  </Heading>
@@ -270,8 +185,8 @@ export const Article = ({
270
185
  competenceGoals={competenceGoals}
271
186
  lang={lang}
272
187
  />
273
- <ArticleContent padded>{content}</ArticleContent>
274
- <ArticleFooter padded>
188
+ <ArticleContent>{content}</ArticleContent>
189
+ <ArticleFooter>
275
190
  <ArticleByline
276
191
  footnotes={footNotes}
277
192
  authors={authors}
@@ -12,10 +12,8 @@ export {
12
12
  ArticleHeader,
13
13
  ArticleFooter,
14
14
  ArticleTitle,
15
- ArticleActionWrapper,
16
15
  ArticleHGroup,
17
16
  ArticleContent,
18
- ArticlePadding,
19
17
  } from "./Article";
20
18
 
21
19
  export { ArticleByline, ArticleBylineAccordionItem } from "./ArticleByline";
@@ -36,7 +36,9 @@ interface Props {
36
36
 
37
37
  const Container = styled("div", {
38
38
  base: {
39
+ width: "100%",
39
40
  display: "flex",
41
+ gap: "medium",
40
42
  flexDirection: "column",
41
43
  border: "1px solid",
42
44
  borderColor: "stroke.default",
@@ -59,17 +61,33 @@ const LinkText = styled(Text, {
59
61
  _hover: {
60
62
  textDecoration: "none",
61
63
  },
64
+ paddingBlock: "xsmall",
65
+ fontWeight: "bold",
66
+ },
67
+ });
68
+
69
+ const LinkHeader = styled(Text, {
70
+ base: {
71
+ display: "flex",
72
+ textDecoration: "underline",
73
+ _hover: {
74
+ textDecoration: "none",
75
+ },
62
76
  },
63
77
  });
78
+
64
79
  const StyledImg = styled("img", {
65
80
  base: {
66
81
  alignSelf: "center",
67
82
  objectFit: "cover",
68
83
  width: "100%",
69
84
  height: "215px",
70
- desktop: {
85
+ mobileWide: {
71
86
  height: "340px",
72
87
  },
88
+ tabletWide: {
89
+ width: "auto",
90
+ },
73
91
  },
74
92
  });
75
93
 
@@ -79,13 +97,26 @@ const ContentWrapper = styled("div", {
79
97
  position: "relative",
80
98
  display: "flex",
81
99
  flexDirection: "column",
82
- gap: "xsmall",
100
+ gap: "medium",
83
101
  alignItems: "flex-start",
102
+ justifyContent: "center",
84
103
  paddingBlock: "medium",
85
104
  paddingInline: "medium",
86
105
  },
87
106
  });
88
107
 
108
+ const StyledText = styled(Text, {
109
+ base: {
110
+ tabletWide: {
111
+ display: "block",
112
+ overflow: "hidden",
113
+ position: "relative",
114
+ lineClamp: 4,
115
+ boxOrient: "vertical",
116
+ },
117
+ },
118
+ });
119
+
89
120
  interface MaybeLinkTextProps {
90
121
  url?: string;
91
122
  path?: string;
@@ -114,7 +145,7 @@ const CampaignBlock = ({
114
145
  className,
115
146
  }: Props) => {
116
147
  const imageComponent = image && <StyledImg src={`${image.src}?width=455`} height={340} width={455} alt={image.alt} />;
117
- const HeaderComponent = url?.url ? LinkText : Text;
148
+ const HeaderComponent = url?.url ? LinkHeader : Text;
118
149
  return (
119
150
  <Container className={className} data-embed-type="campaign-block">
120
151
  {imageSide === "left" && imageComponent}
@@ -124,7 +155,7 @@ const CampaignBlock = ({
124
155
  <InternalHeading>{parse(title)}</InternalHeading>
125
156
  </HeaderComponent>
126
157
  </MaybeLinkText>
127
- <Text textStyle="body.large">{parse(description)}</Text>
158
+ <StyledText textStyle="body.xlarge">{parse(description)}</StyledText>
128
159
  {!!url?.url && (
129
160
  <MaybeLinkText url={url.url} path={path}>
130
161
  <LinkText textStyle="body.medium">
@@ -17,6 +17,8 @@ import { styled } from "@ndla/styled-system/jsx";
17
17
 
18
18
  import * as contentTypes from "../model/ContentType";
19
19
 
20
+ // TODO: Remove this component
21
+
20
22
  interface Props extends ComponentPropsWithoutRef<"div"> {
21
23
  size?: "xx-small" | "x-small" | "small" | "large";
22
24
  type: string;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) 2023-present, NDLA.
2
+ * Copyright (c) 2024-present, NDLA.
3
3
  *
4
4
  * This source code is licensed under the GPLv3 license found in the
5
5
  * LICENSE file in the root directory of this source tree.
@@ -7,42 +7,34 @@
7
7
  */
8
8
 
9
9
  import { Dispatch, SetStateAction, useEffect, useRef, useState } from "react";
10
- import { isMobile } from "react-device-detect";
11
10
  import { useTranslation } from "react-i18next";
12
- import styled from "@emotion/styled";
13
11
  import { Meta, StoryFn } from "@storybook/react";
14
- import { IconButtonV2 } from "@ndla/button";
15
- import { spacing } from "@ndla/core";
16
- import { Spinner } from "@ndla/icons";
17
12
  import { CloseLine } from "@ndla/icons/action";
18
13
  import { CheckLine } from "@ndla/icons/editor";
19
- import { FieldErrorMessage, FieldLabel, FieldRoot, InputContainer, FieldHelper, FieldInput } from "@ndla/primitives";
14
+ import {
15
+ FieldErrorMessage,
16
+ FieldInput,
17
+ FieldLabel,
18
+ FieldRoot,
19
+ FieldHelper,
20
+ IconButton,
21
+ InputContainer,
22
+ Spinner,
23
+ } from "@ndla/primitives";
24
+ import { HStack, styled } from "@ndla/styled-system/jsx";
20
25
  import { IFolder } from "@ndla/types-backend/myndla-api";
21
- import { uuid } from "@ndla/util";
22
26
  import { flattenFolders } from "./helperFunctions";
23
- import TreeStructure, { TreeStructureProps } from "./TreeStructure";
27
+ import { TreeStructure, TreeStructureProps } from "./TreeStructure";
24
28
 
25
- const MY_FOLDERS_ID = "folders";
26
-
27
- const Container = styled.div`
28
- display: flex;
29
- margin-top: 40px;
30
- max-width: 600px;
31
- &[data-type="picker"] {
32
- height: 250px;
33
- }
34
- `;
35
-
36
- const Row = styled.div`
37
- display: flex;
38
- align-items: center;
39
- gap: ${spacing.xxsmall};
40
- padding-right: ${spacing.xsmall};
41
- `;
29
+ const Container = styled("div", {
30
+ base: {
31
+ display: "flex",
32
+ maxWidth: "surface.large",
33
+ maxHeight: "surface.small",
34
+ },
35
+ });
42
36
 
43
- const StyledSpinner = styled(Spinner)`
44
- margin: ${spacing.small};
45
- `;
37
+ const MY_FOLDERS_ID = "folders";
46
38
 
47
39
  const targetResource: TreeStructureProps["targetResource"] = {
48
40
  id: "test-resource",
@@ -147,7 +139,6 @@ export default {
147
139
  targetResource: targetResource,
148
140
  label: "Velg mappe",
149
141
  maxLevel: 5,
150
- type: "picker",
151
142
  // eslint-disable-next-line no-console
152
143
  onSelectFolder: console.log,
153
144
  },
@@ -157,25 +148,19 @@ export default {
157
148
  } as Meta<typeof TreeStructure>;
158
149
 
159
150
  export const Default: StoryFn<typeof TreeStructure> = ({ ...args }) => {
160
- const [structure, setStructure] = useState<IFolder[]>(
161
- args.type === "picker" ? FOLDER_TREE_STRUCTURE : STRUCTURE_EXAMPLE,
162
- );
163
-
164
- useEffect(() => {
165
- setStructure(args.type === "picker" ? FOLDER_TREE_STRUCTURE : STRUCTURE_EXAMPLE);
166
- }, [args.type]);
151
+ const [structure, setStructure] = useState<IFolder[]>(FOLDER_TREE_STRUCTURE);
167
152
 
168
153
  return (
169
- <Container data-type={args.type}>
154
+ <Container>
170
155
  <TreeStructure
171
156
  {...args}
172
157
  folders={structure}
173
- newFolderInput={({ parentId, onClose, onCreate }) => (
158
+ newFolderInput={({ parentId, onCancel, onCreate }) => (
174
159
  <NewFolder
175
160
  structure={structure}
176
161
  setStructure={setStructure}
177
162
  parentId={parentId}
178
- onClose={onClose}
163
+ onCancel={onCancel}
179
164
  onCreate={onCreate}
180
165
  />
181
166
  )}
@@ -188,8 +173,8 @@ interface NewFolderProps {
188
173
  parentId: string;
189
174
  structure: IFolder[];
190
175
  setStructure: Dispatch<SetStateAction<IFolder[]>>;
191
- onClose?: () => void;
192
- onCreate?: (folder: IFolder, parentId: string) => void;
176
+ onCancel?: () => void;
177
+ onCreate?: (folder: IFolder) => void;
193
178
  }
194
179
 
195
180
  const generateNewFolder = (name: string, id: string, breadcrumbs: { id: string; name: string }[]): IFolder => ({
@@ -203,7 +188,7 @@ const generateNewFolder = (name: string, id: string, breadcrumbs: { id: string;
203
188
  updated: "2023-03-03T08:40:23.444Z",
204
189
  });
205
190
 
206
- const NewFolder = ({ parentId, onClose, structure, setStructure, onCreate }: NewFolderProps) => {
191
+ const NewFolder = ({ parentId, onCancel, structure, setStructure, onCreate }: NewFolderProps) => {
207
192
  const [name, setName] = useState("");
208
193
  const [loading, setLoading] = useState(false);
209
194
  const [error, setError] = useState("");
@@ -212,12 +197,6 @@ const NewFolder = ({ parentId, onClose, structure, setStructure, onCreate }: New
212
197
 
213
198
  const inputRef = useRef<HTMLInputElement | null>(null);
214
199
 
215
- useEffect(() => {
216
- if (isMobile) {
217
- inputRef.current?.scrollIntoView({ behavior: "smooth" });
218
- }
219
- }, []);
220
-
221
200
  const onSave = async () => {
222
201
  if (error) {
223
202
  return;
@@ -230,7 +209,7 @@ const NewFolder = ({ parentId, onClose, structure, setStructure, onCreate }: New
230
209
  setLoading(false);
231
210
  const flattenedStructure = flattenFolders(structure);
232
211
  const targetFolder = flattenedStructure.find((folder) => folder.id === parentId);
233
- const newFolderId = uuid();
212
+ const newFolderId = (flattenedStructure.length + 1).toString();
234
213
  const newFolder = generateNewFolder(name, newFolderId, targetFolder?.breadcrumbs ?? []);
235
214
  if (targetFolder) {
236
215
  setStructure((oldStructure) => {
@@ -240,8 +219,7 @@ const NewFolder = ({ parentId, onClose, structure, setStructure, onCreate }: New
240
219
  } else {
241
220
  setStructure((old) => [newFolder].concat(old));
242
221
  }
243
- onCreate?.(newFolder, parentId);
244
- onClose?.();
222
+ onCreate?.(newFolder);
245
223
  };
246
224
 
247
225
  useEffect(() => {
@@ -273,39 +251,31 @@ const NewFolder = ({ parentId, onClose, structure, setStructure, onCreate }: New
273
251
  onKeyDown={(e) => {
274
252
  if (e.key === "Escape") {
275
253
  e.preventDefault();
276
- onClose?.();
254
+ onCancel?.();
277
255
  } else if (e.key === "Enter") {
278
256
  e.preventDefault();
279
257
  onSave();
280
258
  }
281
259
  }}
282
260
  />
283
- <Row>
261
+ <HStack gap="3xsmall">
284
262
  {!loading ? (
285
263
  <>
286
264
  {!error && (
287
- <IconButtonV2
288
- variant={"ghost"}
289
- colorTheme="light"
290
- tabIndex={0}
291
- aria-label={t("save")}
292
- title={t("save")}
293
- size="small"
294
- onClick={onSave}
295
- >
265
+ <IconButton variant="tertiary" aria-label={t("save")} title={t("save")} onClick={onSave}>
296
266
  <CheckLine />
297
- </IconButtonV2>
267
+ </IconButton>
298
268
  )}
299
- <IconButtonV2 aria-label={t("close")} title={t("close")} size="small" variant="ghost" onClick={onClose}>
269
+ <IconButton aria-label={t("close")} title={t("close")} variant="tertiary" onClick={onCancel}>
300
270
  <CloseLine />
301
- </IconButtonV2>
271
+ </IconButton>
302
272
  </>
303
273
  ) : (
304
274
  <FieldHelper>
305
- <StyledSpinner size="normal" aria-label={t("loading")} />
275
+ <Spinner size="small" aria-label={t("loading")} />
306
276
  </FieldHelper>
307
277
  )}
308
- </Row>
278
+ </HStack>
309
279
  </InputContainer>
310
280
  </FieldRoot>
311
281
  );