@mattilsynet/design 3.2.9 → 3.3.1

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 (98) hide show
  1. package/mtds/ai/AGENTS.md +892 -0
  2. package/mtds/ai/alert.mdx +63 -0
  3. package/mtds/ai/alert.stories.tsx +128 -0
  4. package/mtds/ai/analytics.mdx +185 -0
  5. package/mtds/ai/app.mdx +60 -0
  6. package/mtds/ai/app.stories.tsx +897 -0
  7. package/mtds/ai/atlas.mdx +82 -0
  8. package/mtds/ai/atlas.stories.tsx +424 -0
  9. package/mtds/ai/avatar.mdx +45 -0
  10. package/mtds/ai/avatar.stories.tsx +109 -0
  11. package/mtds/ai/badge.mdx +70 -0
  12. package/mtds/ai/badge.stories.tsx +122 -0
  13. package/mtds/ai/breadcrumbs.mdx +36 -0
  14. package/mtds/ai/breadcrumbs.stories.tsx +158 -0
  15. package/mtds/ai/button.mdx +179 -0
  16. package/mtds/ai/button.stories.tsx +440 -0
  17. package/mtds/ai/card.mdx +51 -0
  18. package/mtds/ai/card.stories.tsx +469 -0
  19. package/mtds/ai/chart.mdx +67 -0
  20. package/mtds/ai/chart.stories.tsx +519 -0
  21. package/mtds/ai/chip.mdx +71 -0
  22. package/mtds/ai/chip.stories.tsx +211 -0
  23. package/mtds/ai/details.mdx +33 -0
  24. package/mtds/ai/details.stories.tsx +91 -0
  25. package/mtds/ai/dialog.mdx +38 -0
  26. package/mtds/ai/dialog.stories.tsx +373 -0
  27. package/mtds/ai/divider.mdx +19 -0
  28. package/mtds/ai/divider.stories.tsx +50 -0
  29. package/mtds/ai/errorsummary.mdx +26 -0
  30. package/mtds/ai/errorsummary.stories.tsx +137 -0
  31. package/mtds/ai/field.mdx +86 -0
  32. package/mtds/ai/field.stories.tsx +863 -0
  33. package/mtds/ai/fieldset.mdx +126 -0
  34. package/mtds/ai/fieldset.stories.tsx +298 -0
  35. package/mtds/ai/fileupload.mdx +16 -0
  36. package/mtds/ai/fileupload.stories.tsx +126 -0
  37. package/mtds/ai/helptext.mdx +24 -0
  38. package/mtds/ai/helptext.stories.tsx +106 -0
  39. package/mtds/ai/input.mdx +223 -0
  40. package/mtds/ai/input.stories.tsx +352 -0
  41. package/mtds/ai/law.mdx +115 -0
  42. package/mtds/ai/law.stories.tsx +168 -0
  43. package/mtds/ai/layout.mdx +145 -0
  44. package/mtds/ai/layout.stories.tsx +443 -0
  45. package/mtds/ai/link.mdx +45 -0
  46. package/mtds/ai/link.stories.tsx +44 -0
  47. package/mtds/ai/logo.mdx +86 -0
  48. package/mtds/ai/logo.stories.tsx +146 -0
  49. package/mtds/ai/pagination.mdx +136 -0
  50. package/mtds/ai/pagination.stories.tsx +404 -0
  51. package/mtds/ai/popover.mdx +86 -0
  52. package/mtds/ai/popover.stories.tsx +355 -0
  53. package/mtds/ai/print.mdx +96 -0
  54. package/mtds/ai/print.stories.tsx +839 -0
  55. package/mtds/ai/progress.mdx +41 -0
  56. package/mtds/ai/progress.stories.tsx +141 -0
  57. package/mtds/ai/skeleton.mdx +26 -0
  58. package/mtds/ai/skeleton.stories.tsx +131 -0
  59. package/mtds/ai/spinner.mdx +26 -0
  60. package/mtds/ai/spinner.stories.tsx +72 -0
  61. package/mtds/ai/steps.mdx +37 -0
  62. package/mtds/ai/steps.stories.tsx +568 -0
  63. package/mtds/ai/table.mdx +124 -0
  64. package/mtds/ai/table.stories.tsx +1715 -0
  65. package/mtds/ai/tabs.mdx +106 -0
  66. package/mtds/ai/tabs.stories.tsx +159 -0
  67. package/mtds/ai/tag.mdx +49 -0
  68. package/mtds/ai/tag.stories.tsx +111 -0
  69. package/mtds/ai/toast.mdx +67 -0
  70. package/mtds/ai/toast.stories.tsx +215 -0
  71. package/mtds/ai/togglegroup.mdx +75 -0
  72. package/mtds/ai/togglegroup.stories.tsx +96 -0
  73. package/mtds/ai/tooltip.mdx +32 -0
  74. package/mtds/ai/tooltip.stories.tsx +34 -0
  75. package/mtds/ai/typography.mdx +67 -0
  76. package/mtds/ai/typography.stories.tsx +798 -0
  77. package/mtds/ai/validation.mdx +19 -0
  78. package/mtds/ai/validation.stories.tsx +45 -0
  79. package/mtds/app/app-observer.js +1 -1
  80. package/mtds/app/app-toggle.js +10 -26
  81. package/mtds/app/app-toggle.js.map +1 -1
  82. package/mtds/app/app-toggle2.js +26 -10
  83. package/mtds/app/app-toggle2.js.map +1 -1
  84. package/mtds/app/app.js +1 -1
  85. package/mtds/atlas/atlas-element.js +1 -1
  86. package/mtds/chart/chart-lines.js +19 -19
  87. package/mtds/chart/chart-lines.js.map +1 -1
  88. package/mtds/chart/chart.css.js +16 -1
  89. package/mtds/chart/chart.css.js.map +1 -1
  90. package/mtds/chart/chart.stories.d.ts +1 -0
  91. package/mtds/index.iife.js +32 -17
  92. package/mtds/index.js +21 -20
  93. package/mtds/index.js.map +1 -1
  94. package/mtds/package.json.js +1 -1
  95. package/mtds/styles.css +1 -1
  96. package/mtds/table/table-observer.js +26 -15
  97. package/mtds/table/table-observer.js.map +1 -1
  98. package/package.json +4 -2
@@ -0,0 +1,126 @@
1
+ import { Meta, Canvas } from '@storybook/addon-docs/blocks';
2
+ import { Example, CssVariables } from '../../.storybook/blocks';
3
+ import * as stories from './fieldset.stories';
4
+ import { Flex, Grid } from '../react';
5
+ import styles from '../styles.module.css';
6
+
7
+ <Meta of={stories} />
8
+
9
+ # Fieldset
10
+ >Fieldset brukes til å gruppere og navngi felt som naturlig hører sammen, for eksempel datofelt eller adressefelt. Komponenten hjelper med å organisere informasjon, gjøre skjemaer mer oversiktlige og forbedre tilgjengeligheten for skjermlesere.
11
+
12
+ ## Kode
13
+ - Legg klassen `fieldset` på `<fieldset>` rundt `<legend>` og skjemafelter
14
+ - Dette grupperer typisk `checkbox` eller `radio` inputs
15
+ <Canvas of={stories.Radios} />
16
+
17
+ ## Skikk og bruk
18
+ <Flex data-items="350">
19
+ {/* Vertikal vs horisontal alignment: */}
20
+ <Example data-color="success" text="Radiobutton- og checkboxgrupper skal i hovedsak stables vertikalt slik at alternativene er lette å lese." zoom="100%">
21
+ <div className={styles.grid}>
22
+ <label>Velg dyregruppe</label>
23
+ <ds-field className={styles.field}>
24
+ <label>Fisk</label>
25
+ <input type="radio" className={styles.input} name="my-radio" defaultChecked />
26
+ </ds-field>
27
+ <ds-field className={styles.field}>
28
+ <label>Fjørfe</label>
29
+ <input type="radio" className={styles.input} name="my-radio" />
30
+ </ds-field>
31
+ <ds-field className={styles.field}>
32
+ <label>Storfe</label>
33
+ <input type="radio" className={styles.input} name="my-radio" />
34
+ </ds-field>
35
+ </div>
36
+ </Example>
37
+ <Example data-color="danger" text="Radiobutton- og checkboxgrupper bør ikke legges etter hverandre. Da er det vanskeligere skanne innholdet" zoom="100%">
38
+ <div className={styles.grid}>
39
+ <label>Velg dyregruppe</label>
40
+ <Flex data-gap="10">
41
+ <ds-field className={styles.field}>
42
+ <label>Fisk</label>
43
+ <input type="radio" className={styles.input} name="my-radio" defaultChecked />
44
+ </ds-field>
45
+ <ds-field className={styles.field}>
46
+ <label>Fjørfe</label>
47
+ <input type="radio" className={styles.input} name="my-radio" />
48
+ </ds-field>
49
+ <ds-field className={styles.field}>
50
+ <label>Storfe</label>
51
+ <input type="radio" className={styles.input} name="my-radio" />
52
+ </ds-field>
53
+ </Flex>
54
+ </div>
55
+ </Example>
56
+
57
+ {/* Label + Input: */}
58
+ <Example data-color="success" text="Input, radiobutton- og checkboxgrupper skal alltid ha en tydelig label." zoom="100%">
59
+ <div className={styles.grid}>
60
+ <label>Velg størrelse</label>
61
+ <ds-field className={styles.field}>
62
+ <label>Liten</label>
63
+ <input type="radio" className={styles.input} name="my-radio" defaultChecked />
64
+ </ds-field>
65
+ <ds-field className={styles.field}>
66
+ <label>Medium</label>
67
+ <input type="radio" className={styles.input} name="my-radio" />
68
+ </ds-field>
69
+ <ds-field className={styles.field}>
70
+ <label>Stor</label>
71
+ <input type="radio" className={styles.input} name="my-radio" />
72
+ </ds-field>
73
+ </div>
74
+ </Example>
75
+
76
+ <Example data-color="danger" text="Uten en label kan alternativene være vanskelige eller umulige å forstå." zoom="100%">
77
+ <Grid>
78
+ <ds-field className={styles.field}>
79
+ <label>Liten</label>
80
+ <input type="radio" className={styles.input} name="my-radio" defaultChecked />
81
+ </ds-field>
82
+ <ds-field className={styles.field}>
83
+ <label>Medium</label>
84
+ <input type="radio" className={styles.input} name="my-radio" />
85
+ </ds-field>
86
+ <ds-field className={styles.field}>
87
+ <label>Stor</label>
88
+ <input type="radio" className={styles.input} name="my-radio" />
89
+ </ds-field>
90
+ </Grid>
91
+ </Example>
92
+ </Flex>
93
+
94
+ ## Obligatoriske felter
95
+ - Dersom alle skjemafelter er påkrevd, vises dette automatisk i `<legend>`
96
+ - Teksten `Må fylles ut` oversettes automatisk til `Required` dersom du setter `<html lang="en">`. Du kan også selv endre tekst med CSS custom property: `--mtds-text-required: "Må fylles ut";`
97
+ - **Merk:** Dersom du ønsker å skru denne [lovpålagte merkingen av obligatoriske felter](https://www.designsystemet.no/monstre/obligatoriske-og-valgfrie-felt), kan du legge `data-required="hidden"` på hvilket som helst element (f.eks. `<html data-required="hidden">` for å skru av for hele dokumentet)
98
+ <Canvas of={stories.Radios} />
99
+
100
+ ## Innebygget skjemavalidering
101
+ - Nettleserens innebyggede valideringsmeldinger skjules pga. begrenset styling og universell utforming
102
+ - Du kan allikevel få [nettleserens innebygge skjemavalidering](https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Forms/Form_validation#using_built-in_form_validation) til å automatisk skjule og vise [validation-komponentet](?path=/docs/designsystem-validation--docs) ved å legge `data-validation="form"` på `fieldset`
103
+ - **Merk:** Legg du vil vise valideringsmelding ved hvert input-element, må `data-validation="form"` legges på hvert enkelt `field`
104
+ <Canvas of={stories.WithValidationForm} />
105
+
106
+ ## Checkboxes
107
+ <Canvas of={stories.Checkboxes} />
108
+
109
+ ## Disabled
110
+ - Legg attributten `disabled` rett på `fieldset`
111
+ <Canvas of={stories.Disabled} />
112
+
113
+ ## Read only
114
+ - Legg attributten `readonly disabled` på hver `input`
115
+ <Canvas of={stories.ReadOnly} />
116
+
117
+ {/* ## Horisontal NOT RECOMMENDED
118
+ - Legg attributten `field` inni en `flex` med `data-gap="9"`
119
+ - Brukes kun ved få svaralternativer
120
+ <Canvas of={stories.Horizontal} /> */}
121
+
122
+ ## Feilmelding
123
+ - Legg klassen `validation` på et direkte barn for å knytte feilmelding til alle inputs
124
+ <Canvas of={stories.WithValidation} />
125
+
126
+ <CssVariables component="fieldset" />
@@ -0,0 +1,298 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Field, Fieldset } from "../react";
3
+ import styles from "../styles.module.css";
4
+
5
+ const meta = {
6
+ title: "Designsystem/Fieldset",
7
+ } satisfies Meta;
8
+
9
+ export default meta;
10
+ type Story = StoryObj<typeof meta>;
11
+
12
+ export const React: Story = {
13
+ render: () => (
14
+ <Fieldset>
15
+ <Fieldset.Legend>Hva foretrekker du?</Fieldset.Legend>
16
+ <Fieldset.Description>Fellesbeskrivelse</Fieldset.Description>
17
+ <Field
18
+ as="input"
19
+ defaultChecked
20
+ description="Beskrivelse"
21
+ label="Alternativ 1"
22
+ name="my-react-radio"
23
+ required
24
+ type="radio"
25
+ />
26
+ <Field
27
+ as="input"
28
+ label="Alternativ 2"
29
+ name="my-react-radio"
30
+ required
31
+ type="radio"
32
+ />
33
+ </Fieldset>
34
+ ),
35
+ };
36
+
37
+ export const Radios: Story = {
38
+ parameters: {
39
+ showInOverview: true,
40
+ },
41
+ render: () => (
42
+ <fieldset className={styles.fieldset}>
43
+ <legend>Hva foretrekker du?</legend>
44
+ <p data-field="description">Fellesbeskrivelse</p>
45
+ <ds-field className={styles.field}>
46
+ <input
47
+ type="radio"
48
+ className={styles.input}
49
+ name="my-radio"
50
+ required
51
+ defaultChecked
52
+ />
53
+ <label>Alternativ 1</label>
54
+ <p data-field="description">Beskrivelse</p>
55
+ </ds-field>
56
+ <ds-field className={styles.field}>
57
+ <input type="radio" className={styles.input} name="my-radio" required />
58
+ <label>Alternativ 2</label>
59
+ <p data-field="description">Beskrivelse</p>
60
+ </ds-field>
61
+ </fieldset>
62
+ ),
63
+ };
64
+
65
+ export const Checkboxes: Story = {
66
+ parameters: {
67
+ showInOverview: true,
68
+ },
69
+ render: () => (
70
+ <fieldset className={styles.fieldset}>
71
+ <legend>Hvilke foretrekker du?</legend>
72
+ <p data-field="description">Fellesbeskrivelse</p>
73
+ <ds-field className={styles.field}>
74
+ <input
75
+ type="checkbox"
76
+ className={styles.input}
77
+ name="my-check"
78
+ defaultChecked
79
+ />
80
+ <label>Alternativ 1</label>
81
+ </ds-field>
82
+ <ds-field className={styles.field}>
83
+ <input type="checkbox" className={styles.input} name="my-check" />
84
+ <label>Alternativ 2</label>
85
+ </ds-field>
86
+ </fieldset>
87
+ ),
88
+ };
89
+
90
+ export const Disabled: Story = {
91
+ render: () => (
92
+ <fieldset className={styles.fieldset} disabled>
93
+ <legend>Hvilke foretrekker du?</legend>
94
+ <p data-field="description">Fellesbeskrivelse</p>
95
+ <ds-field className={styles.field}>
96
+ <input
97
+ type="checkbox"
98
+ className={styles.input}
99
+ name="my-check"
100
+ defaultChecked
101
+ />
102
+ <label>Alternativ 1</label>
103
+ </ds-field>
104
+ <ds-field className={styles.field}>
105
+ <input type="checkbox" className={styles.input} name="my-check" />
106
+ <label>Alternativ 2</label>
107
+ </ds-field>
108
+ </fieldset>
109
+ ),
110
+ };
111
+
112
+ export const ReadOnly: Story = {
113
+ render: () => (
114
+ <fieldset className={styles.fieldset}>
115
+ <legend>Hvilke foretrekker du?</legend>
116
+ <p data-field="description">Fellesbeskrivelse</p>
117
+ <ds-field className={styles.field}>
118
+ <input
119
+ type="checkbox"
120
+ className={styles.input}
121
+ name="my-check"
122
+ defaultChecked
123
+ readOnly
124
+ disabled
125
+ />
126
+ <label>Alternativ 1</label>
127
+ </ds-field>
128
+ <ds-field className={styles.field}>
129
+ <input
130
+ type="checkbox"
131
+ className={styles.input}
132
+ name="my-check"
133
+ readOnly
134
+ disabled
135
+ />
136
+ <label>Alternativ 2</label>
137
+ </ds-field>
138
+ </fieldset>
139
+ ),
140
+ };
141
+
142
+ export const Horizontal: Story = {
143
+ render: () => (
144
+ <fieldset className={styles.fieldset}>
145
+ <legend>Hvilke foretrekker du?</legend>
146
+ <p data-field="description">Fellesbeskrivelse</p>
147
+ <div className={styles.flex} data-gap="9">
148
+ <ds-field className={styles.field}>
149
+ <input
150
+ type="checkbox"
151
+ className={styles.input}
152
+ name="my-check"
153
+ defaultChecked
154
+ />
155
+ <label>Alternativ 1</label>
156
+ </ds-field>
157
+ <ds-field className={styles.field}>
158
+ <input type="checkbox" className={styles.input} name="my-check" />
159
+ <label>Alternativ 2</label>
160
+ </ds-field>
161
+ </div>
162
+ </fieldset>
163
+ ),
164
+ };
165
+
166
+ export const WithValidation: Story = {
167
+ parameters: {
168
+ showInOverview: true,
169
+ },
170
+ render: () => (
171
+ <fieldset className={styles.fieldset}>
172
+ <legend>Hvilke foretrekker du?</legend>
173
+ <p data-field="description">Fellesbeskrivelse</p>
174
+ <ds-field className={styles.field}>
175
+ <input
176
+ type="checkbox"
177
+ className={styles.input}
178
+ name="my-check"
179
+ defaultChecked
180
+ />
181
+ <label>Alternativ 1</label>
182
+ <p data-field="description">Beskrivelse</p>
183
+ </ds-field>
184
+ <ds-field className={styles.field}>
185
+ <input type="checkbox" className={styles.input} name="my-check" />
186
+ <label>Alternativ 2</label>
187
+ </ds-field>
188
+ <div className={styles.validation} data-field="validation">
189
+ Feilmelding
190
+ </div>
191
+ </fieldset>
192
+ ),
193
+ };
194
+
195
+ export const WithValidationForm: Story = {
196
+ parameters: {
197
+ showInOverview: true,
198
+ },
199
+ render: () => (
200
+ <form action="#" className={styles.prose} data-validation="form">
201
+ <fieldset className={styles.fieldset}>
202
+ <legend>Hvilke foretrekker du?</legend>
203
+ <ds-field className={styles.field}>
204
+ <input
205
+ type="checkbox"
206
+ className={styles.input}
207
+ name="my-check-validation"
208
+ required
209
+ />
210
+ <label>Checkbox 1</label>
211
+ <div className={styles.validation} data-field="validation" hidden>
212
+ Feilmelding 1
213
+ </div>
214
+ </ds-field>
215
+ <ds-field className={styles.field}>
216
+ <input
217
+ type="checkbox"
218
+ className={styles.input}
219
+ name="my-check-validation"
220
+ required
221
+ />
222
+ <label>Checkbox 2</label>
223
+ <div className={styles.validation} data-field="validation" hidden>
224
+ Feilmelding 2
225
+ </div>
226
+ </ds-field>
227
+ <div className={styles.validation} data-field="validation" hidden>
228
+ Feilmelding
229
+ </div>
230
+ </fieldset>
231
+ <fieldset className={styles.fieldset}>
232
+ <legend>Hvilke foretrekker du?</legend>
233
+ <ds-field className={styles.field}>
234
+ <input
235
+ type="radio"
236
+ className={styles.input}
237
+ name="my-radio-validation"
238
+ required
239
+ />
240
+ <label>Radio 1</label>
241
+ <div className={styles.validation} data-field="validation" hidden>
242
+ Feilmelding 1
243
+ </div>
244
+ </ds-field>
245
+ <ds-field className={styles.field}>
246
+ <input
247
+ type="radio"
248
+ className={styles.input}
249
+ name="my-radio-validation"
250
+ required
251
+ />
252
+ <label>Radio 2</label>
253
+ <div className={styles.validation} data-field="validation" hidden>
254
+ Feilmelding 2
255
+ </div>
256
+ </ds-field>
257
+ <div className={styles.validation} data-field="validation" hidden>
258
+ Feilmelding
259
+ </div>
260
+ </fieldset>
261
+
262
+ <button type="submit" className={styles.button} data-variant="primary">
263
+ Send inn
264
+ </button>
265
+ </form>
266
+ ),
267
+ };
268
+
269
+ export const WithTexts: Story = {
270
+ parameters: {
271
+ showInOverview: true,
272
+ },
273
+ render: () => (
274
+ <fieldset className={styles.fieldset}>
275
+ <legend>Hva heter du?</legend>
276
+ <div className={styles.grid} data-items="200" data-fixed>
277
+ <ds-field className={styles.field}>
278
+ <input
279
+ aria-label="Fornavn"
280
+ placeholder="Fornavn"
281
+ className={styles.input}
282
+ name="fornavn"
283
+ type="text"
284
+ />
285
+ </ds-field>
286
+ <ds-field className={styles.field}>
287
+ <input
288
+ aria-label="Etternavn"
289
+ placeholder="Etternavn"
290
+ className={styles.input}
291
+ name="etternavn"
292
+ type="text"
293
+ />
294
+ </ds-field>
295
+ </div>
296
+ </fieldset>
297
+ ),
298
+ };
@@ -0,0 +1,16 @@
1
+ import { Meta, Canvas } from '@storybook/addon-docs/blocks';
2
+ import { Example, CssVariables } from '../../.storybook/blocks';
3
+ import * as stories from './fileupload.stories';
4
+ import { Flex, Grid } from '../react';
5
+ import styles from '../styles.module.css';
6
+
7
+ <Meta of={stories} />
8
+
9
+ # Fileupload (Eksperimentell)
10
+
11
+ ## Kode
12
+ - Legg klassen `fileupload` på `<label>` rundt `<input type="file">`
13
+ - Du kan selv knytte til funkjonalitet med JavaScript
14
+ <Canvas of={stories.Default} />
15
+
16
+ <CssVariables component="fileupload" />
@@ -0,0 +1,126 @@
1
+ import { UploadIcon, XIcon } from "@phosphor-icons/react";
2
+ import type { Meta, StoryObj } from "@storybook/react-vite";
3
+ import { useState } from "react";
4
+ import { Button, Card, Fileupload, Flex, Grid } from "../react";
5
+ import styles from "../styles.module.css";
6
+
7
+ const meta = {
8
+ title: "Designsystem/Fileupload",
9
+ parameters: {
10
+ layout: "padded",
11
+ },
12
+ } satisfies Meta;
13
+
14
+ export default meta;
15
+ type Story = StoryObj<typeof meta>;
16
+
17
+ export const Default: Story = {
18
+ render: () => (
19
+ <label className={styles.fileupload}>
20
+ <UploadIcon />
21
+ <strong>Last opp filer</strong>
22
+ Dra over eller <u>last opp fil</u>
23
+ <small>
24
+ Støttede formater: PDF, JPEG, SVG
25
+ <br />
26
+ Filstørrelse under 10 MB
27
+ </small>
28
+ <input type="file" accept="image/*,.pdf" title="Last opp filer" />
29
+ </label>
30
+ ),
31
+ };
32
+
33
+ export const React: Story = {
34
+ render: () => (
35
+ <Fileupload>
36
+ <UploadIcon />
37
+ <strong>Last opp filer</strong>
38
+ Dra over eller <u>last opp fil</u>
39
+ <small>
40
+ Støttede formater: PDF, JPEG, SVG
41
+ <br />
42
+ Filstørrelse under 10 MB
43
+ </small>
44
+ <input type="file" accept="image/*,.pdf" title="Last opp filer" />
45
+ </Fileupload>
46
+ ),
47
+ };
48
+
49
+ export const Invalid: Story = {
50
+ render: () => (
51
+ <Fileupload aria-invalid="true">
52
+ <UploadIcon />
53
+ <strong>Last opp filer</strong>
54
+ Dra over eller <u>last opp fil</u>
55
+ <small>
56
+ Støttede formater: PDF, JPEG, SVG
57
+ <br />
58
+ Filstørrelse under 10 MB
59
+ </small>
60
+ <input type="file" accept="image/*,.pdf" title="Last opp filer" />
61
+ </Fileupload>
62
+ ),
63
+ };
64
+
65
+ export const WithPreviewImage: Story = {
66
+ render: function Render() {
67
+ const [images, setImages] = useState<{ title: string; src: string }[]>([]);
68
+ const handleFile = (event: React.ChangeEvent<HTMLInputElement>) => {
69
+ const file = event.target.files?.[0];
70
+ const reader = new FileReader();
71
+ reader.onload = () =>
72
+ setImages((prev) => [
73
+ ...prev,
74
+ {
75
+ title: file?.name || "",
76
+ src: reader.result as string,
77
+ },
78
+ ]);
79
+ if (file) reader.readAsDataURL(file);
80
+ event.target.value = ""; // Reset file input value to allow re-uploading the same file
81
+ };
82
+
83
+ const handleRemove = (index: number) =>
84
+ setImages((prev) => prev.filter((_, i) => i !== index));
85
+
86
+ return (
87
+ <Grid>
88
+ Velg et bilde for å se:
89
+ <Fileupload>
90
+ <UploadIcon />
91
+ <strong>Last opp filer</strong>
92
+ Dra over eller <u>last opp fil</u>
93
+ <small>
94
+ Støttede formater: PDF, JPEG, SVG
95
+ <br />
96
+ Filstørrelse under 10 MB
97
+ </small>
98
+ <input
99
+ type="file"
100
+ accept="image/jpeg, image/png, image/gif"
101
+ title="Last opp filer"
102
+ onChange={handleFile}
103
+ />
104
+ </Fileupload>
105
+ <Grid data-items="300" data-fixed>
106
+ {images.map((file, index) => (
107
+ <Card as={Grid} key={index.toString()}>
108
+ <img alt="" width="100%" src={file.src} />
109
+ <Flex data-align="center" data-nowrap>
110
+ <strong>{file.title}</strong>
111
+ <Button
112
+ data-self="auto"
113
+ data-fixed
114
+ data-size="sm"
115
+ onClick={() => handleRemove(index)}
116
+ >
117
+ <XIcon />
118
+ </Button>
119
+ </Flex>
120
+ </Card>
121
+ ))}
122
+ </Grid>
123
+ </Grid>
124
+ );
125
+ },
126
+ };
@@ -0,0 +1,24 @@
1
+ import { Meta, Canvas } from '@storybook/addon-docs/blocks';
2
+ import { CssVariables } from '../../.storybook/blocks';
3
+ import * as stories from './helptext.stories';
4
+
5
+ <Meta of={stories} />
6
+
7
+ # Help text
8
+ >HelpText gir brukere en forklaring på ukjente begreper eller konsepter, hvis de trenger det.
9
+
10
+ ## Kode
11
+ - **Merk:** Prøv å [unngå å bruke HelpText](https://www.designsystemet.no/no/blog/helptext-is-being-removed-what-should-you-do) - vurder heller [popover inline](?path=/story/designsystem-popover--with-text-inline)
12
+ - Bruk klassen `helptext` på `<button>` og `popovertarget="MIN-ID"`
13
+ - Legg klassen `popover`, attributten `popover` og `id` på `<div>` direkte etter `<button>`
14
+ <Canvas of={stories.Default} />
15
+
16
+ ## I field
17
+ - Legg `helptext` like etter `label` i `field`
18
+ <Canvas of={stories.InField} />
19
+
20
+ ## I fieldset
21
+ - Legg `helptext` like etter `legend` i `field`
22
+ <Canvas of={stories.InFieldset} />
23
+
24
+ <CssVariables component="helptext" />
@@ -0,0 +1,106 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Field, Grid, HelpText, Input } from "../react";
3
+ import styles from "../styles.module.css";
4
+
5
+ const meta = {
6
+ title: "Designsystem/Help text",
7
+ } satisfies Meta;
8
+
9
+ export default meta;
10
+ type Story = StoryObj<typeof meta>;
11
+
12
+ export const Default: Story = {
13
+ render: () => (
14
+ <>
15
+ <button
16
+ type="button"
17
+ className={styles.helptext}
18
+ popoverTarget="my-helptext"
19
+ aria-label="Hva menes med mottaker"
20
+ ></button>
21
+ <div className={styles.popover} id="my-helptext" popover="auto">
22
+ Tekst som forklarer hva som menes med mottaker
23
+ </div>
24
+ </>
25
+ ),
26
+ };
27
+
28
+ export const InField: Story = {
29
+ render: () => (
30
+ <div className={styles.grid}>
31
+ <ds-field className={styles.field}>
32
+ <label>Ledetekst</label>
33
+ <button
34
+ type="button"
35
+ className={styles.helptext}
36
+ aria-label="Hva menes med mottaker"
37
+ popoverTarget="my-helptext-field"
38
+ ></button>
39
+ <div className={styles.popover} id="my-helptext-field" popover="auto">
40
+ Tekst som forklarer hva som menes med mottaker
41
+ </div>
42
+ <p data-field="desciption">Beskrivelse</p>
43
+ <input type="text" className={styles.input} />
44
+ </ds-field>
45
+ <ds-field className={styles.field}>
46
+ <label>Ledetekst</label>
47
+ <button
48
+ type="button"
49
+ className={styles.helptext}
50
+ aria-label="Hva menes med mottaker"
51
+ popoverTarget="my-helptext-field"
52
+ ></button>
53
+ <div className={styles.popover} id="my-helptext-field" popover="auto">
54
+ Tekst som forklarer hva som menes med mottaker
55
+ </div>
56
+ <p data-field="desciption">Beskrivelse</p>
57
+ <input type="text" className={styles.input} readOnly disabled />
58
+ </ds-field>
59
+ </div>
60
+ ),
61
+ };
62
+
63
+ export const InFieldset: Story = {
64
+ render: () => (
65
+ <fieldset className={styles.fieldset}>
66
+ <legend>Legendtekst</legend>
67
+ <button
68
+ type="button"
69
+ className={styles.helptext}
70
+ aria-label="Hva menes med mottaker"
71
+ popoverTarget="my-helptext-fieldset"
72
+ ></button>
73
+ <div className={styles.popover} id="my-helptext-fieldset" popover="auto">
74
+ Tekst som forklarer hva som menes med mottaker
75
+ </div>
76
+ <ds-field className={styles.field}>
77
+ <label>Labeltekst</label>
78
+ <input type="checkbox" className={styles.input} />
79
+ </ds-field>
80
+ </fieldset>
81
+ ),
82
+ };
83
+
84
+ export const React: Story = {
85
+ render: () => (
86
+ <Grid data-gap="10">
87
+ <HelpText aria-label="Hva menes med mottaker">
88
+ Tekst som forklarer hva som menes med mottaker
89
+ </HelpText>
90
+ <Field>
91
+ <Field.Label>Ledetekst</Field.Label>
92
+ <HelpText aria-label="Hva menes med mottaker">
93
+ Tekst som forklarer hva som menes med mottaker
94
+ </HelpText>
95
+ <Field.Description>Beskrivelse</Field.Description>
96
+ <Input />
97
+ </Field>
98
+ <Field
99
+ as="input"
100
+ label="Ledetekst"
101
+ helpText="Tekst som forklarer hva som menes med mottaker"
102
+ helpTextLabel="Hva menes med mottaker"
103
+ />
104
+ </Grid>
105
+ ),
106
+ };