@ks-digital/designsystem-angular 0.0.1-alpha.23 → 0.0.1-alpha.25
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/.storybook/customTheme.ts +15 -0
- package/.storybook/default-args.ts +18 -0
- package/.storybook/main.ts +27 -0
- package/.storybook/manager.ts +10 -0
- package/.storybook/preview-head.html +16 -0
- package/.storybook/preview.ts +70 -0
- package/.storybook/themes.ts +9 -0
- package/.storybook/tsconfig.json +16 -0
- package/.storybook/vite.config.mts +5 -0
- package/README.md +3 -3
- package/eslint.config.mjs +28 -0
- package/ng-package.json +9 -0
- package/package.json +18 -27
- package/project.json +81 -0
- package/src/components/alert/alert.mdx +46 -0
- package/src/components/alert/alert.spec.ts +33 -0
- package/src/components/alert/alert.stories.ts +138 -0
- package/src/components/alert/alert.ts +46 -0
- package/src/components/alert/index.ts +1 -0
- package/src/components/button/button.mdx +40 -0
- package/src/components/button/button.spec.ts +86 -0
- package/src/components/button/button.stories.ts +123 -0
- package/src/components/button/button.ts +60 -0
- package/src/components/button/index.ts +1 -0
- package/src/components/card/card-block.ts +10 -0
- package/src/components/card/card.mdx +100 -0
- package/src/components/card/card.spec.ts +70 -0
- package/src/components/card/card.stories.ts +101 -0
- package/src/components/card/card.ts +44 -0
- package/src/components/card/index.ts +2 -0
- package/src/components/checkbox/README.md +13 -0
- package/src/components/checkbox/checkbox.mdx +50 -0
- package/src/components/checkbox/checkbox.spec.ts +21 -0
- package/src/components/checkbox/checkbox.stories.ts +182 -0
- package/src/components/checkbox/index.ts +0 -0
- package/src/components/colors.ts +36 -0
- package/src/components/common-inputs.ts +30 -0
- package/src/components/details/controlled-details.ts +63 -0
- package/src/components/details/details-content.ts +7 -0
- package/src/components/details/details-summary.ts +7 -0
- package/src/components/details/details.mdx +89 -0
- package/src/components/details/details.spec.ts +56 -0
- package/src/components/details/details.stories.ts +129 -0
- package/src/components/details/details.ts +69 -0
- package/src/components/details/index.ts +3 -0
- package/src/components/field/field-counter.ts +56 -0
- package/src/components/field/field-description.ts +10 -0
- package/src/components/field/field-error.ts +13 -0
- package/src/components/field/field-observer.ts +121 -0
- package/src/components/field/field-state.ts +21 -0
- package/src/components/field/field.mdx +40 -0
- package/src/components/field/field.spec.ts +131 -0
- package/src/components/field/field.stories.ts +98 -0
- package/src/components/field/field.ts +70 -0
- package/src/components/field/index.ts +3 -0
- package/src/components/fieldset/fieldset-description.ts +8 -0
- package/src/components/fieldset/fieldset-legend.ts +11 -0
- package/src/components/fieldset/fieldset.spec.ts +80 -0
- package/src/components/fieldset/fieldset.ts +11 -0
- package/src/components/fieldset/index.ts +3 -0
- package/src/components/input/index.ts +1 -0
- package/src/components/input/input.mdx +11 -0
- package/src/components/input/input.spec.ts +25 -0
- package/src/components/input/input.stories.ts +72 -0
- package/src/components/input/input.ts +67 -0
- package/src/components/label/index.ts +1 -0
- package/src/components/label/label.ts +17 -0
- package/src/components/paragraph/index.ts +1 -0
- package/src/components/paragraph/paragraph.ts +10 -0
- package/src/components/popover/controlled-popover.ts +62 -0
- package/src/components/popover/index.ts +1 -0
- package/src/components/popover/popover.mdx +81 -0
- package/src/components/popover/popover.spec.ts +143 -0
- package/src/components/popover/popover.stories.ts +63 -0
- package/src/components/popover/popover.ts +186 -0
- package/src/components/radio/radio.mdx +117 -0
- package/src/components/radio/radio.stories.ts +226 -0
- package/src/components/search/index.ts +4 -0
- package/src/components/search/search-button.ts +35 -0
- package/src/components/search/search-clear.ts +57 -0
- package/src/components/search/search-input.ts +18 -0
- package/src/components/search/search.mdx +56 -0
- package/src/components/search/search.spec.ts +48 -0
- package/src/components/search/search.stories.ts +205 -0
- package/src/components/search/search.ts +50 -0
- package/src/components/spinner/index.ts +1 -0
- package/src/components/spinner/spinner.mdx +24 -0
- package/src/components/spinner/spinner.spec.ts +13 -0
- package/src/components/spinner/spinner.stories.ts +54 -0
- package/src/components/spinner/spinner.ts +62 -0
- package/src/components/switch/switch.mdx +82 -0
- package/src/components/switch/switch.stories.ts +94 -0
- package/src/components/textarea/textarea.mdx +14 -0
- package/src/components/textarea/textarea.stories.ts +52 -0
- package/src/components/validation-message/index.ts +1 -0
- package/src/components/validation-message/validation-message.ts +11 -0
- package/src/index.ts +14 -0
- package/src/test-setup.ts +12 -0
- package/src/utils/log-if-devmode.ts +13 -0
- package/src/utils/random-id.ts +3 -0
- package/tsconfig.json +34 -0
- package/tsconfig.lib.json +28 -0
- package/tsconfig.lib.prod.json +9 -0
- package/tsconfig.spec.json +30 -0
- package/vite.config.mts +35 -0
- package/dist/README.md +0 -55
- package/dist/fesm2022/ks-digital-designsystem-angular.mjs +0 -1068
- package/dist/fesm2022/ks-digital-designsystem-angular.mjs.map +0 -1
- package/dist/index.d.ts +0 -315
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Meta, Primary, Controls, Story } from '@storybook/addon-docs/blocks'
|
|
2
|
+
|
|
3
|
+
import * as CheckboxStories from './checkbox.stories'
|
|
4
|
+
|
|
5
|
+
<Meta of={CheckboxStories} />
|
|
6
|
+
|
|
7
|
+
# Checkbox
|
|
8
|
+
|
|
9
|
+
Vi bruker `Checkbox` for å gi brukerne valg, der de kan velge ett eller flere alternativer. Brukerne kan både velge og oppheve valgene de gjør. Bruk [`Fieldset`](#fieldset) til å gruppere flere valg.
|
|
10
|
+
|
|
11
|
+
<Primary />
|
|
12
|
+
<Controls />
|
|
13
|
+
|
|
14
|
+
## Bruk
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import {
|
|
18
|
+
Input,
|
|
19
|
+
Field,
|
|
20
|
+
Fieldset,
|
|
21
|
+
FieldsetDescription,
|
|
22
|
+
FieldsetLegend,
|
|
23
|
+
Label,
|
|
24
|
+
ValidationMessage,
|
|
25
|
+
} from '@ks-digital/designsystem-angular'
|
|
26
|
+
;<fieldset ksd-fieldset>
|
|
27
|
+
<legend ksd-fieldset-legend>
|
|
28
|
+
Hvordan vil du helst at vi skal kontakte deg?
|
|
29
|
+
</legend>
|
|
30
|
+
|
|
31
|
+
<p ksd-fieldset-description>
|
|
32
|
+
Velg alle alternativene som er relevante for deg.
|
|
33
|
+
</p>
|
|
34
|
+
|
|
35
|
+
<ksd-field>
|
|
36
|
+
<ksd-label> E-post </ksd-label>
|
|
37
|
+
<input ksd-input type="checkbox" value="e-post" />
|
|
38
|
+
</ksd-field>
|
|
39
|
+
|
|
40
|
+
<ksd-field>
|
|
41
|
+
<ksd-label> Telefon </ksd-label>
|
|
42
|
+
<input ksd-input type="checkbox" value="telefon" />
|
|
43
|
+
</ksd-field>
|
|
44
|
+
|
|
45
|
+
<ksd-field>
|
|
46
|
+
<ksd-label> SMS </ksd-label>
|
|
47
|
+
<input ksd-input type="checkbox" value="sms" />
|
|
48
|
+
</ksd-field>
|
|
49
|
+
</fieldset>
|
|
50
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/angular'
|
|
2
|
+
import userEvent from '@testing-library/user-event'
|
|
3
|
+
import { Field } from '../field/field'
|
|
4
|
+
import { Input } from '../input/input'
|
|
5
|
+
import { Label } from '../label/label'
|
|
6
|
+
|
|
7
|
+
test('should not be clickable if readonly', async () => {
|
|
8
|
+
await render(
|
|
9
|
+
`
|
|
10
|
+
<ksd-field>
|
|
11
|
+
<ksd-label>My checkbox</ksd-label>
|
|
12
|
+
<input ksd-input type="checkbox" value="my-checkbox" readonly />
|
|
13
|
+
</ksd-field>
|
|
14
|
+
`,
|
|
15
|
+
{ imports: [Field, Input, Label] },
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
const checkbox = screen.getByRole('checkbox') as HTMLInputElement
|
|
19
|
+
await userEvent.click(checkbox)
|
|
20
|
+
expect(checkbox.checked).toBe(false)
|
|
21
|
+
})
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import {
|
|
2
|
+
argsToTemplate,
|
|
3
|
+
moduleMetadata,
|
|
4
|
+
type Meta,
|
|
5
|
+
type StoryObj,
|
|
6
|
+
} from '@storybook/angular'
|
|
7
|
+
import { CommonArgs } from '../../../.storybook/default-args'
|
|
8
|
+
import { Field } from '../field/field'
|
|
9
|
+
import { FieldDescription } from '../field/field-description'
|
|
10
|
+
import { Fieldset } from '../fieldset/fieldset'
|
|
11
|
+
import { FieldsetDescription } from '../fieldset/fieldset-description'
|
|
12
|
+
import { FieldsetLegend } from '../fieldset/fieldset-legend'
|
|
13
|
+
import { Input } from '../input/input'
|
|
14
|
+
import { Label } from '../label/label'
|
|
15
|
+
|
|
16
|
+
type InputArgs = CommonArgs & {
|
|
17
|
+
readonly: boolean
|
|
18
|
+
disabled: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const meta: Meta<Input> = {
|
|
22
|
+
component: Input,
|
|
23
|
+
title: 'Komponenter/Checkbox',
|
|
24
|
+
decorators: [
|
|
25
|
+
moduleMetadata({
|
|
26
|
+
imports: [
|
|
27
|
+
Input,
|
|
28
|
+
Label,
|
|
29
|
+
Field,
|
|
30
|
+
FieldDescription,
|
|
31
|
+
Fieldset,
|
|
32
|
+
FieldsetDescription,
|
|
33
|
+
FieldsetLegend,
|
|
34
|
+
],
|
|
35
|
+
}),
|
|
36
|
+
],
|
|
37
|
+
}
|
|
38
|
+
export default meta
|
|
39
|
+
type Story = StoryObj<InputArgs>
|
|
40
|
+
|
|
41
|
+
export const Preview: Story = {
|
|
42
|
+
args: {
|
|
43
|
+
readonly: false,
|
|
44
|
+
disabled: false,
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
render: (args) => ({
|
|
48
|
+
props: args,
|
|
49
|
+
template: `
|
|
50
|
+
<ksd-field>
|
|
51
|
+
<ksd-label> Checkbox label </ksd-label>
|
|
52
|
+
<input ksd-input type="checkbox" value="some-value" ${argsToTemplate(args)} />
|
|
53
|
+
<p ksd-field-description>Description</p>
|
|
54
|
+
</ksd-field>
|
|
55
|
+
`,
|
|
56
|
+
}),
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const Group: Story = {
|
|
60
|
+
args: {
|
|
61
|
+
...Preview.args,
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
render: (args) => ({
|
|
65
|
+
props: args,
|
|
66
|
+
template: `
|
|
67
|
+
<fieldset ksd-fieldset>
|
|
68
|
+
<legend ksd-fieldset-legend>
|
|
69
|
+
Hvordan vil du helst at vi skal kontakte deg?
|
|
70
|
+
</legend>
|
|
71
|
+
<p ksd-fieldset-description>
|
|
72
|
+
Velg alle alternativene som er relevante for deg.
|
|
73
|
+
</p>
|
|
74
|
+
|
|
75
|
+
<ksd-field>
|
|
76
|
+
<ksd-label> E-post </ksd-label>
|
|
77
|
+
<input ksd-input type="checkbox" value="e-post" ${argsToTemplate(args)} />
|
|
78
|
+
</ksd-field>
|
|
79
|
+
|
|
80
|
+
<ksd-field>
|
|
81
|
+
<ksd-label> Telefon </ksd-label>
|
|
82
|
+
<input ksd-input type="checkbox" value="telefon" ${argsToTemplate(args)} />
|
|
83
|
+
</ksd-field>
|
|
84
|
+
|
|
85
|
+
<ksd-field>
|
|
86
|
+
<ksd-label> SMS </ksd-label>
|
|
87
|
+
<input ksd-input type="checkbox" value="sms" ${argsToTemplate(args)} />
|
|
88
|
+
</ksd-field>
|
|
89
|
+
|
|
90
|
+
</fieldset>
|
|
91
|
+
`,
|
|
92
|
+
}),
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const AriaLabel: Story = {
|
|
96
|
+
args: {
|
|
97
|
+
...Preview.args,
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
render: (args) => ({
|
|
101
|
+
props: args,
|
|
102
|
+
template: `
|
|
103
|
+
<ksd-field>
|
|
104
|
+
<input ksd-input type="checkbox" value="some-value" aria-label="Checkbox label" ${argsToTemplate(args)} />
|
|
105
|
+
</ksd-field>
|
|
106
|
+
`,
|
|
107
|
+
}),
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const ReadOnly: Story = {
|
|
111
|
+
args: {
|
|
112
|
+
...Preview.args,
|
|
113
|
+
readonly: true,
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
render: (args) => ({
|
|
117
|
+
props: args,
|
|
118
|
+
template: `
|
|
119
|
+
<fieldset ksd-fieldset>
|
|
120
|
+
<legend ksd-fieldset-legend>
|
|
121
|
+
Hvordan vil du helst at vi skal kontakte deg?
|
|
122
|
+
</legend>
|
|
123
|
+
<p ksd-fieldset-description>
|
|
124
|
+
Velg alle alternativene som er relevante for deg.
|
|
125
|
+
</p>
|
|
126
|
+
|
|
127
|
+
<ksd-field>
|
|
128
|
+
<ksd-label> E-post </ksd-label>
|
|
129
|
+
<input ksd-input type="checkbox" value="e-post" ${argsToTemplate(args)} />
|
|
130
|
+
</ksd-field>
|
|
131
|
+
|
|
132
|
+
<ksd-field>
|
|
133
|
+
<ksd-label> Telefon </ksd-label>
|
|
134
|
+
<input ksd-input type="checkbox" value="telefon" ${argsToTemplate(args)} />
|
|
135
|
+
</ksd-field>
|
|
136
|
+
|
|
137
|
+
<ksd-field>
|
|
138
|
+
<ksd-label> SMS </ksd-label>
|
|
139
|
+
<input ksd-input type="checkbox" value="sms" ${argsToTemplate(args)} />
|
|
140
|
+
</ksd-field>
|
|
141
|
+
|
|
142
|
+
</fieldset>
|
|
143
|
+
`,
|
|
144
|
+
}),
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export const Disabled: Story = {
|
|
148
|
+
args: {
|
|
149
|
+
...Preview.args,
|
|
150
|
+
disabled: true,
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
render: (args) => ({
|
|
154
|
+
props: args,
|
|
155
|
+
template: `
|
|
156
|
+
<fieldset ksd-fieldset>
|
|
157
|
+
<legend ksd-fieldset-legend>
|
|
158
|
+
Hvordan vil du helst at vi skal kontakte deg?
|
|
159
|
+
</legend>
|
|
160
|
+
<p ksd-fieldset-description>
|
|
161
|
+
Velg alle alternativene som er relevante for deg.
|
|
162
|
+
</p>
|
|
163
|
+
|
|
164
|
+
<ksd-field>
|
|
165
|
+
<ksd-label> E-post </ksd-label>
|
|
166
|
+
<input ksd-input type="checkbox" value="e-post" ${argsToTemplate(args)} />
|
|
167
|
+
</ksd-field>
|
|
168
|
+
|
|
169
|
+
<ksd-field>
|
|
170
|
+
<ksd-label> Telefon </ksd-label>
|
|
171
|
+
<input ksd-input type="checkbox" value="telefon" ${argsToTemplate(args)} />
|
|
172
|
+
</ksd-field>
|
|
173
|
+
|
|
174
|
+
<ksd-field>
|
|
175
|
+
<ksd-label> SMS </ksd-label>
|
|
176
|
+
<input ksd-input type="checkbox" value="sms" ${argsToTemplate(args)} />
|
|
177
|
+
</ksd-field>
|
|
178
|
+
|
|
179
|
+
</fieldset>
|
|
180
|
+
`,
|
|
181
|
+
}),
|
|
182
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Lifted from https://github.com/digdir/designsystemet/blob/main/packages/react/src/colors.ts
|
|
3
|
+
**/
|
|
4
|
+
|
|
5
|
+
// EmptyObject implementation from https://github.com/sindresorhus/type-fest/blob/main/source/empty-object.d.ts
|
|
6
|
+
declare const emptyObjectSymbol: unique symbol
|
|
7
|
+
type EmptyObject = { [emptyObjectSymbol]?: never }
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Base interface for available colors in the design system.
|
|
11
|
+
* The CLI will generate augmentations of this interface to allow
|
|
12
|
+
* type safety of custom color names.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// eslint-disable-next-line
|
|
16
|
+
export interface MainAndSupportColors {}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* If {@link MainAndSupportColors} has been extended to include color names, return T,
|
|
20
|
+
* otherwise return the arbitrary string type.
|
|
21
|
+
*/
|
|
22
|
+
type ColorWithFallback<T> = MainAndSupportColors extends EmptyObject
|
|
23
|
+
? string
|
|
24
|
+
: T
|
|
25
|
+
|
|
26
|
+
export type SeverityInfo = 'info'
|
|
27
|
+
export type SeveritySuccess = 'success'
|
|
28
|
+
export type SeverityWarning = 'warning'
|
|
29
|
+
export type SeverityDanger = 'danger'
|
|
30
|
+
export type SeverityColors =
|
|
31
|
+
| SeverityInfo
|
|
32
|
+
| SeveritySuccess
|
|
33
|
+
| SeverityWarning
|
|
34
|
+
| SeverityDanger
|
|
35
|
+
|
|
36
|
+
export type Color = ColorWithFallback<'neutral' | keyof MainAndSupportColors>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* eslint-disable @angular-eslint/no-input-rename */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* We use input aliasing to bridge the gap between Angular's camelCase property naming convention and our HTML data attributes.
|
|
5
|
+
* This approach allows us to use valid HTML data attributes as documented by Designsystemet while maintaining
|
|
6
|
+
* proper TypeScript intellisense support.
|
|
7
|
+
*
|
|
8
|
+
* Todo: Some components are using only a subset of colors, e.g., SeverityColors for Alert. We should reconsider this directive
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Directive, input } from '@angular/core'
|
|
12
|
+
import { Color } from './colors'
|
|
13
|
+
|
|
14
|
+
export type Size = 'sm' | 'md' | 'lg'
|
|
15
|
+
|
|
16
|
+
@Directive()
|
|
17
|
+
export class CommonInputs {
|
|
18
|
+
/**
|
|
19
|
+
* Changes size for descendant Designsystemet components. Select from predefined sizes.
|
|
20
|
+
* @attribute data-size
|
|
21
|
+
*/
|
|
22
|
+
dataSize = input<Size>(undefined, { alias: 'data-size' })
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Changes color for descendant Designsystemet components.
|
|
26
|
+
* Select from predefined colors and colors defined using theme.designsystemet.no.
|
|
27
|
+
* @attribute data-color
|
|
28
|
+
*/
|
|
29
|
+
dataColor = input<Color>(undefined, { alias: 'data-color' })
|
|
30
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Component, computed, signal } from '@angular/core'
|
|
2
|
+
import { Button } from '../button'
|
|
3
|
+
import { Details } from './details'
|
|
4
|
+
import { DetailsContent } from './details-content'
|
|
5
|
+
import { DetailsSummary } from './details-summary'
|
|
6
|
+
|
|
7
|
+
@Component({
|
|
8
|
+
selector: 'fiks-controlled-details',
|
|
9
|
+
imports: [Details, DetailsContent, DetailsSummary, Button],
|
|
10
|
+
template: `
|
|
11
|
+
<button
|
|
12
|
+
ksd-button
|
|
13
|
+
variant="secondary"
|
|
14
|
+
style="margin-bottom: .5em;"
|
|
15
|
+
(click)="toggleOpen()"
|
|
16
|
+
>
|
|
17
|
+
{{ toggleOpenText() }}
|
|
18
|
+
</button>
|
|
19
|
+
<ksd-details [open]="open1()" (toggled)="open1.set(!open1())">
|
|
20
|
+
<ksd-details-summary>Enkeltpersonforetak</ksd-details-summary>
|
|
21
|
+
<ksd-details-content>
|
|
22
|
+
Skal du starte for deg selv? Enkeltpersonforetak er ofte den enkleste
|
|
23
|
+
måten å etablere bedrift på. Denne organisasjonsformen har både fordeler
|
|
24
|
+
og ulemper. Det gir deg stor grad av frihet, men kan også gi betydelig
|
|
25
|
+
risiko fordi du har personlig ansvar for økonomien.
|
|
26
|
+
</ksd-details-content>
|
|
27
|
+
</ksd-details>
|
|
28
|
+
<ksd-details [open]="open2()" (toggled)="open2.set(!open2())">
|
|
29
|
+
<ksd-details-summary>Aksjeselskap (AS)</ksd-details-summary>
|
|
30
|
+
<ksd-details-content>
|
|
31
|
+
Planlegger du å starte næringsvirksomhet alene eller sammen med andre?
|
|
32
|
+
Innebærer næringsvirksomheten en økonomisk risiko? Vil du ha rettigheter
|
|
33
|
+
som arbeidstaker og muligheten til at andre kan investere i selskapet
|
|
34
|
+
ditt? Da kan aksjeselskap være en hensiktsmessig organisasjonsform.
|
|
35
|
+
</ksd-details-content>
|
|
36
|
+
</ksd-details>
|
|
37
|
+
<ksd-details [open]="open3()" (toggled)="open3.set(!open3())">
|
|
38
|
+
<ksd-details-summary>Ansvarlig selskap (ANS/DA)</ksd-details-summary>
|
|
39
|
+
<ksd-details-content>
|
|
40
|
+
Skal dere starte opp egen virksomhet sammen? Samarbeider dere godt?
|
|
41
|
+
Krever virksomheten små investeringer og innebærer liten økonomisk
|
|
42
|
+
risiko? Da kan ansvarlig selskap være aktuelt.
|
|
43
|
+
</ksd-details-content>
|
|
44
|
+
</ksd-details>
|
|
45
|
+
`,
|
|
46
|
+
})
|
|
47
|
+
export class ControlledDetails {
|
|
48
|
+
open1 = signal(false)
|
|
49
|
+
open2 = signal(false)
|
|
50
|
+
open3 = signal(false)
|
|
51
|
+
|
|
52
|
+
isOpen = computed(() =>
|
|
53
|
+
[this.open1(), this.open2(), this.open3()].every(Boolean),
|
|
54
|
+
)
|
|
55
|
+
toggleOpenText = computed(() => (this.isOpen() ? 'Lukk alle' : 'Åpne alle'))
|
|
56
|
+
|
|
57
|
+
toggleOpen = () => {
|
|
58
|
+
const isOpen = this.isOpen()
|
|
59
|
+
this.open1.set(!isOpen)
|
|
60
|
+
this.open2.set(!isOpen)
|
|
61
|
+
this.open3.set(!isOpen)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Meta, Canvas, Controls, Primary } from '@storybook/addon-docs/blocks'
|
|
2
|
+
import * as DetailsStories from './details.stories'
|
|
3
|
+
|
|
4
|
+
<Meta of={DetailsStories} />
|
|
5
|
+
|
|
6
|
+
# Details
|
|
7
|
+
|
|
8
|
+
Med `Details` kan du presentera mykje innhald på liten plass i ei eller fleire rader. Heile rada er klikkbar og let brukarar opna eller lukka visninga av innhaldet under.
|
|
9
|
+
|
|
10
|
+
<Primary />
|
|
11
|
+
<Controls />
|
|
12
|
+
|
|
13
|
+
## Bruk
|
|
14
|
+
|
|
15
|
+
```html
|
|
16
|
+
/* Utan ramme */
|
|
17
|
+
<ksd-details>
|
|
18
|
+
<ksd-details-summary>Vedlegg</ksd-details-summary>
|
|
19
|
+
<ksd-details-content>Vedlegg 1, vedlegg 2, vedlegg 3</ksd-details-content>
|
|
20
|
+
</ksd-details>
|
|
21
|
+
|
|
22
|
+
/* Med ramme */
|
|
23
|
+
|
|
24
|
+
<article ksd-card>
|
|
25
|
+
<ksd-details>
|
|
26
|
+
<ksd-details-summary>Vedlegg</ksd-details-summary>
|
|
27
|
+
<ksd-details-content>Vedlegg 1, vedlegg 2, vedlegg 3</ksd-details-content>
|
|
28
|
+
</ksd-details>
|
|
29
|
+
</article>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Kodeeksempler
|
|
33
|
+
|
|
34
|
+
### Med ramme
|
|
35
|
+
|
|
36
|
+
`Details` kan visast med ramme ved å leggje den i eit `Card`.
|
|
37
|
+
Dette kan passa i tilfelle der `Details` ikkje fyller heile sida, eller når det berre er ei rad.
|
|
38
|
+
|
|
39
|
+
<Canvas of={DetailsStories.InCard} />
|
|
40
|
+
|
|
41
|
+
### Med fargar
|
|
42
|
+
|
|
43
|
+
`Details` kan visast i fargar frå temaet ditt.
|
|
44
|
+
|
|
45
|
+
<Canvas of={DetailsStories.InCardWithColor} />
|
|
46
|
+
|
|
47
|
+
#### Kontrollert
|
|
48
|
+
|
|
49
|
+
`Details` held sjølv styr på om den er open eller lukka, men dette kan òg kontrollerast utanfrå.
|
|
50
|
+
|
|
51
|
+
<Canvas of={DetailsStories.Controlled} />
|
|
52
|
+
|
|
53
|
+
## Retningslinjer
|
|
54
|
+
|
|
55
|
+
Ikkje bruk `Details` til å skjula innhald for å gjera sida "ryddigare". Når vi skjuler innhold er det fare for at brukerne ikke se innholdet i det hele tatt. Finn ut om du faktisk må skjula innhald og ver klar over kvifor du gjer det.
|
|
56
|
+
|
|
57
|
+
**Passar til å**
|
|
58
|
+
|
|
59
|
+
- samla innhald
|
|
60
|
+
- gjera det frivillig å sjå innhald som er mindre viktig
|
|
61
|
+
- visa tilleggsinformasjon som kan vera til hjelp for brukarane
|
|
62
|
+
|
|
63
|
+
**Passar ikkje til å**
|
|
64
|
+
|
|
65
|
+
- visa viktig innhald som alle bør sjå når dei kjem til sida
|
|
66
|
+
- oppsummera feilmeldingar - bruk heller [`ErrorSummary`](/docs/komponenter-errorsummary--docs)
|
|
67
|
+
|
|
68
|
+
### Unngå nøsta lister
|
|
69
|
+
|
|
70
|
+
Ikkje legg ein `Details` inni ein annan, det vi kallar nøsta lister. Det kan bli forvirrande for brukaren å forstå kva som er opna og lukka, spesielt når fleire nivå kan opnast samtidig.
|
|
71
|
+
|
|
72
|
+
## Tekst
|
|
73
|
+
|
|
74
|
+
Sørg for at overskrifta gjev ei god skildring av kva innhaldet i `Details` er. Overskriftene kan ha stor tyding for om brukarane finn det dei treng, om innhaldet blir lese og om det kan reknast som tilgjengeleg for alle brukarar. «Vis meir» eller «Les meir her» er ikkje gode nok titlar. Har du ein `Details` med mange nedtrekk, kan du ha ei hovudoverskrift eller temaoverskrift over heile lista.
|
|
75
|
+
|
|
76
|
+
Hold innholdet i `Details` kort for å sikre at det lett kan relateres til overskriften. Om innhaldet er for langt bør du fordela innhaldet i fleire `Details`. Ved veldig mykje innhald kan det vere betre å vurdere å lage eigne sider.
|
|
77
|
+
|
|
78
|
+
## Tilgjengelegheit
|
|
79
|
+
|
|
80
|
+
[`Chevron`](https://aksel.nav.no/ikoner/ChevronDown)-ikonet er plassert til venstre for teksten, av omsyn til brukarar med nedsett synsfelt. Dei ser berre ein liten del av skjermen om gongen, og skannar gjerne vertikalt nedover på venstre side. Difor bør funksjonelle element stå til venstre, der dei lettare blir oppdaga. Plasseringa av chevronen bør òg vere konsekvent for å unngå forveksling med andre element.
|
|
81
|
+
|
|
82
|
+
Ikkje plasser andre interaktive element inn i `Details.Summary`, då heile rada skal vera klikkbar. Ikonet og teksten skal _ikkje_ lenka til ulike handlingar (til dømes at teksten går vidare til ei side, medan ikonet opnar lista). Brukarane ventar ikkje at ikon og tekst skal gje ulikt resultat når dei vel dei.
|
|
83
|
+
|
|
84
|
+
<kbd>Space</kbd> eller <kbd>Enter</kbd> åpnar eller lukkar `Details`.
|
|
85
|
+
|
|
86
|
+
## Kjende manglar
|
|
87
|
+
|
|
88
|
+
I Firefox og Safari blir det ikkje spelt av animasjon når `Details` blir opna og lukka.
|
|
89
|
+
Sjå [interpolate-size](https://developer.mozilla.org/en-US/docs/Web/CSS/interpolate-size).
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Component } from '@angular/core'
|
|
2
|
+
import { render, screen } from '@testing-library/angular'
|
|
3
|
+
import userEvent from '@testing-library/user-event'
|
|
4
|
+
import { Details } from './details'
|
|
5
|
+
import { DetailsContent } from './details-content'
|
|
6
|
+
import { DetailsSummary } from './details-summary'
|
|
7
|
+
|
|
8
|
+
describe('Details', () => {
|
|
9
|
+
it('should have summary, content and be open when clicked', async () => {
|
|
10
|
+
@Component({
|
|
11
|
+
template: `
|
|
12
|
+
<ksd-details>
|
|
13
|
+
<ksd-details-summary>Details Summary Text</ksd-details-summary>
|
|
14
|
+
<ksd-details-content
|
|
15
|
+
>The fantastic details content text</ksd-details-content
|
|
16
|
+
>
|
|
17
|
+
</ksd-details>
|
|
18
|
+
`,
|
|
19
|
+
imports: [Details, DetailsContent, DetailsSummary],
|
|
20
|
+
})
|
|
21
|
+
class TestDetails {}
|
|
22
|
+
|
|
23
|
+
await render(TestDetails)
|
|
24
|
+
|
|
25
|
+
const user = userEvent.setup()
|
|
26
|
+
const detailsExpandButton = screen.getByRole('button')
|
|
27
|
+
|
|
28
|
+
await user.click(detailsExpandButton)
|
|
29
|
+
|
|
30
|
+
expect(screen.getByText('Details Summary Text'))
|
|
31
|
+
expect(screen.getByText('The fantastic details content text'))
|
|
32
|
+
expect(detailsExpandButton).toHaveAttribute('aria-expanded', 'true')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('should render details with open state as controlled', async () => {
|
|
36
|
+
@Component({
|
|
37
|
+
template: `
|
|
38
|
+
<ksd-details [open]="true" (toggled)="noop()">
|
|
39
|
+
<ksd-details-summary>Details Summary Text</ksd-details-summary>
|
|
40
|
+
<ksd-details-content
|
|
41
|
+
>The fantastic details content text</ksd-details-content
|
|
42
|
+
>
|
|
43
|
+
</ksd-details>
|
|
44
|
+
`,
|
|
45
|
+
imports: [Details, DetailsContent, DetailsSummary],
|
|
46
|
+
})
|
|
47
|
+
class TestDetails {
|
|
48
|
+
noop = () => undefined
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
await render(TestDetails)
|
|
52
|
+
|
|
53
|
+
const detailsExpandButton = screen.getByRole('button')
|
|
54
|
+
expect(detailsExpandButton).toHaveAttribute('aria-expanded', 'true')
|
|
55
|
+
})
|
|
56
|
+
})
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'
|
|
2
|
+
import { Card } from '../card/card'
|
|
3
|
+
import { ControlledDetails } from './controlled-details'
|
|
4
|
+
import { Details } from './details'
|
|
5
|
+
import { DetailsContent } from './details-content'
|
|
6
|
+
import { DetailsSummary } from './details-summary'
|
|
7
|
+
|
|
8
|
+
const meta: Meta<Details> = {
|
|
9
|
+
component: Details,
|
|
10
|
+
title: 'Komponenter/Details',
|
|
11
|
+
parameters: {
|
|
12
|
+
layout: 'padded',
|
|
13
|
+
},
|
|
14
|
+
decorators: [
|
|
15
|
+
moduleMetadata({
|
|
16
|
+
imports: [
|
|
17
|
+
Card,
|
|
18
|
+
Details,
|
|
19
|
+
DetailsContent,
|
|
20
|
+
DetailsSummary,
|
|
21
|
+
ControlledDetails,
|
|
22
|
+
],
|
|
23
|
+
}),
|
|
24
|
+
],
|
|
25
|
+
}
|
|
26
|
+
export default meta
|
|
27
|
+
type Story = StoryObj<Details>
|
|
28
|
+
|
|
29
|
+
export const Preview: Story = {
|
|
30
|
+
args: {},
|
|
31
|
+
parameters: {
|
|
32
|
+
summaryText: 'Vedlegg',
|
|
33
|
+
contentText: 'Vedlegg 1, vedlegg 2, vedlegg 3',
|
|
34
|
+
},
|
|
35
|
+
render: (args, context) => ({
|
|
36
|
+
props: {
|
|
37
|
+
...args,
|
|
38
|
+
summary: context.parameters['summaryText'],
|
|
39
|
+
content: context.parameters['contentText'],
|
|
40
|
+
},
|
|
41
|
+
template: `
|
|
42
|
+
<ksd-details data-testid="details">
|
|
43
|
+
<ksd-details-summary>Vedlegg</ksd-details-summary>
|
|
44
|
+
<ksd-details-content>Vedlegg 1, vedlegg 2, vedlegg 3</ksd-details-content>
|
|
45
|
+
</ksd-details>
|
|
46
|
+
`,
|
|
47
|
+
}),
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const WithoutCard: Story = {
|
|
51
|
+
render: () => ({
|
|
52
|
+
template: `
|
|
53
|
+
<ksd-details>
|
|
54
|
+
<ksd-details-summary>Vedlegg</ksd-details-summary>
|
|
55
|
+
<ksd-details-content>Vedlegg 1, vedlegg 2, vedlegg 3</ksd-details-content>
|
|
56
|
+
</ksd-details>
|
|
57
|
+
`,
|
|
58
|
+
}),
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const InCard: Story = {
|
|
62
|
+
render: () => ({
|
|
63
|
+
template: `
|
|
64
|
+
<article ksd-card>
|
|
65
|
+
<ksd-details>
|
|
66
|
+
<ksd-details-summary>Vedlegg</ksd-details-summary>
|
|
67
|
+
<ksd-details-content>Vedlegg 1, vedlegg 2, vedlegg 3</ksd-details-content>
|
|
68
|
+
</ksd-details>
|
|
69
|
+
</article>
|
|
70
|
+
`,
|
|
71
|
+
}),
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const InCardWithColor: Story = {
|
|
75
|
+
render: () => ({
|
|
76
|
+
template: `
|
|
77
|
+
<div style="display: flex; flex-direction: column; gap: 1rem">
|
|
78
|
+
<article ksd-card>
|
|
79
|
+
<ksd-details data-color="accent">
|
|
80
|
+
<ksd-details-summary>Vedlegg</ksd-details-summary>
|
|
81
|
+
<ksd-details-content>Vedlegg 1, vedlegg 2, vedlegg 3</ksd-details-content>
|
|
82
|
+
</ksd-details>
|
|
83
|
+
</article>
|
|
84
|
+
<article ksd-card>
|
|
85
|
+
<ksd-details data-color="accent" variant="tinted">
|
|
86
|
+
<ksd-details-summary>Vedlegg</ksd-details-summary>
|
|
87
|
+
<ksd-details-content>Vedlegg 1, vedlegg 2, vedlegg 3</ksd-details-content>
|
|
88
|
+
</ksd-details>
|
|
89
|
+
</article>
|
|
90
|
+
</div>
|
|
91
|
+
`,
|
|
92
|
+
}),
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const WithDifferentSizes: Story = {
|
|
96
|
+
render: () => ({
|
|
97
|
+
template: `
|
|
98
|
+
<ksd-details data-size="sm">
|
|
99
|
+
<ksd-details-summary>Vedlegg</ksd-details-summary>
|
|
100
|
+
<ksd-details-content>Vedlegg 1, vedlegg 2, vedlegg 3</ksd-details-content>
|
|
101
|
+
</ksd-details>
|
|
102
|
+
<ksd-details data-size="md">
|
|
103
|
+
<ksd-details-summary>Vedlegg</ksd-details-summary>
|
|
104
|
+
<ksd-details-content>Vedlegg 1, vedlegg 2, vedlegg 3</ksd-details-content>
|
|
105
|
+
</ksd-details>
|
|
106
|
+
<ksd-details data-size="lg">
|
|
107
|
+
<ksd-details-summary>Vedlegg</ksd-details-summary>
|
|
108
|
+
<ksd-details-content>Vedlegg 1, vedlegg 2, vedlegg 3</ksd-details-content>
|
|
109
|
+
</ksd-details>
|
|
110
|
+
`,
|
|
111
|
+
}),
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export const DefaultOpen: Story = {
|
|
115
|
+
render: () => ({
|
|
116
|
+
template: `
|
|
117
|
+
<ksd-details [defaultOpen]="true">
|
|
118
|
+
<ksd-details-summary>Vedlegg</ksd-details-summary>
|
|
119
|
+
<ksd-details-content>Vedlegg 1, vedlegg 2, vedlegg 3</ksd-details-content>
|
|
120
|
+
</ksd-details>
|
|
121
|
+
`,
|
|
122
|
+
}),
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export const Controlled: StoryObj<ControlledDetails> = {
|
|
126
|
+
render: () => ({
|
|
127
|
+
template: `<fiks-controlled-details />`,
|
|
128
|
+
}),
|
|
129
|
+
}
|