@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.
Files changed (109) hide show
  1. package/.storybook/customTheme.ts +15 -0
  2. package/.storybook/default-args.ts +18 -0
  3. package/.storybook/main.ts +27 -0
  4. package/.storybook/manager.ts +10 -0
  5. package/.storybook/preview-head.html +16 -0
  6. package/.storybook/preview.ts +70 -0
  7. package/.storybook/themes.ts +9 -0
  8. package/.storybook/tsconfig.json +16 -0
  9. package/.storybook/vite.config.mts +5 -0
  10. package/README.md +3 -3
  11. package/eslint.config.mjs +28 -0
  12. package/ng-package.json +9 -0
  13. package/package.json +18 -27
  14. package/project.json +81 -0
  15. package/src/components/alert/alert.mdx +46 -0
  16. package/src/components/alert/alert.spec.ts +33 -0
  17. package/src/components/alert/alert.stories.ts +138 -0
  18. package/src/components/alert/alert.ts +46 -0
  19. package/src/components/alert/index.ts +1 -0
  20. package/src/components/button/button.mdx +40 -0
  21. package/src/components/button/button.spec.ts +86 -0
  22. package/src/components/button/button.stories.ts +123 -0
  23. package/src/components/button/button.ts +60 -0
  24. package/src/components/button/index.ts +1 -0
  25. package/src/components/card/card-block.ts +10 -0
  26. package/src/components/card/card.mdx +100 -0
  27. package/src/components/card/card.spec.ts +70 -0
  28. package/src/components/card/card.stories.ts +101 -0
  29. package/src/components/card/card.ts +44 -0
  30. package/src/components/card/index.ts +2 -0
  31. package/src/components/checkbox/README.md +13 -0
  32. package/src/components/checkbox/checkbox.mdx +50 -0
  33. package/src/components/checkbox/checkbox.spec.ts +21 -0
  34. package/src/components/checkbox/checkbox.stories.ts +182 -0
  35. package/src/components/checkbox/index.ts +0 -0
  36. package/src/components/colors.ts +36 -0
  37. package/src/components/common-inputs.ts +30 -0
  38. package/src/components/details/controlled-details.ts +63 -0
  39. package/src/components/details/details-content.ts +7 -0
  40. package/src/components/details/details-summary.ts +7 -0
  41. package/src/components/details/details.mdx +89 -0
  42. package/src/components/details/details.spec.ts +56 -0
  43. package/src/components/details/details.stories.ts +129 -0
  44. package/src/components/details/details.ts +69 -0
  45. package/src/components/details/index.ts +3 -0
  46. package/src/components/field/field-counter.ts +56 -0
  47. package/src/components/field/field-description.ts +10 -0
  48. package/src/components/field/field-error.ts +13 -0
  49. package/src/components/field/field-observer.ts +121 -0
  50. package/src/components/field/field-state.ts +21 -0
  51. package/src/components/field/field.mdx +40 -0
  52. package/src/components/field/field.spec.ts +131 -0
  53. package/src/components/field/field.stories.ts +98 -0
  54. package/src/components/field/field.ts +70 -0
  55. package/src/components/field/index.ts +3 -0
  56. package/src/components/fieldset/fieldset-description.ts +8 -0
  57. package/src/components/fieldset/fieldset-legend.ts +11 -0
  58. package/src/components/fieldset/fieldset.spec.ts +80 -0
  59. package/src/components/fieldset/fieldset.ts +11 -0
  60. package/src/components/fieldset/index.ts +3 -0
  61. package/src/components/input/index.ts +1 -0
  62. package/src/components/input/input.mdx +11 -0
  63. package/src/components/input/input.spec.ts +25 -0
  64. package/src/components/input/input.stories.ts +72 -0
  65. package/src/components/input/input.ts +67 -0
  66. package/src/components/label/index.ts +1 -0
  67. package/src/components/label/label.ts +17 -0
  68. package/src/components/paragraph/index.ts +1 -0
  69. package/src/components/paragraph/paragraph.ts +10 -0
  70. package/src/components/popover/controlled-popover.ts +62 -0
  71. package/src/components/popover/index.ts +1 -0
  72. package/src/components/popover/popover.mdx +81 -0
  73. package/src/components/popover/popover.spec.ts +143 -0
  74. package/src/components/popover/popover.stories.ts +63 -0
  75. package/src/components/popover/popover.ts +186 -0
  76. package/src/components/radio/radio.mdx +117 -0
  77. package/src/components/radio/radio.stories.ts +226 -0
  78. package/src/components/search/index.ts +4 -0
  79. package/src/components/search/search-button.ts +35 -0
  80. package/src/components/search/search-clear.ts +57 -0
  81. package/src/components/search/search-input.ts +18 -0
  82. package/src/components/search/search.mdx +56 -0
  83. package/src/components/search/search.spec.ts +48 -0
  84. package/src/components/search/search.stories.ts +205 -0
  85. package/src/components/search/search.ts +50 -0
  86. package/src/components/spinner/index.ts +1 -0
  87. package/src/components/spinner/spinner.mdx +24 -0
  88. package/src/components/spinner/spinner.spec.ts +13 -0
  89. package/src/components/spinner/spinner.stories.ts +54 -0
  90. package/src/components/spinner/spinner.ts +62 -0
  91. package/src/components/switch/switch.mdx +82 -0
  92. package/src/components/switch/switch.stories.ts +94 -0
  93. package/src/components/textarea/textarea.mdx +14 -0
  94. package/src/components/textarea/textarea.stories.ts +52 -0
  95. package/src/components/validation-message/index.ts +1 -0
  96. package/src/components/validation-message/validation-message.ts +11 -0
  97. package/src/index.ts +14 -0
  98. package/src/test-setup.ts +12 -0
  99. package/src/utils/log-if-devmode.ts +13 -0
  100. package/src/utils/random-id.ts +3 -0
  101. package/tsconfig.json +34 -0
  102. package/tsconfig.lib.json +28 -0
  103. package/tsconfig.lib.prod.json +9 -0
  104. package/tsconfig.spec.json +30 -0
  105. package/vite.config.mts +35 -0
  106. package/dist/README.md +0 -55
  107. package/dist/fesm2022/ks-digital-designsystem-angular.mjs +0 -1068
  108. package/dist/fesm2022/ks-digital-designsystem-angular.mjs.map +0 -1
  109. package/dist/index.d.ts +0 -315
@@ -0,0 +1,40 @@
1
+ import { Meta, Primary, Controls, Story } from '@storybook/addon-docs/blocks'
2
+
3
+ import * as ButtonStories from './button.stories'
4
+
5
+ <Meta of={ButtonStories} />
6
+
7
+ # Button
8
+
9
+ Knappar lèt brukarane utføre handlingar.
10
+
11
+ <Primary />
12
+ <Controls />
13
+
14
+ ## Ikoner
15
+
16
+ - [Unngå å bruke ikonfonter](https://www.irigoyen.dev/blog/2021/02/17/stop-using-icon-fonts/).
17
+ - For Angular kan du bruke [Ng Icons](https://ng-icons.github.io/ng-icons), et bibliotek som inneholder ikoner fra Phosphor Icons og Material Icons – de to ikonsettene vi offisielt støtter i våre løsninger.
18
+
19
+ ### Egne ikoner
20
+
21
+ Du kan også bruke SVG-markup direkte, f.eks. hvis du har egne ikoner.
22
+
23
+ ```tsx
24
+ <button ksd-button>
25
+ <svg
26
+ xmlns="http://www.w3.org/2000/svg"
27
+ width="1em"
28
+ height="1em"
29
+ viewBox="0 0 24 24"
30
+ fill="none"
31
+ stroke="currentColor"
32
+ aria-hidden="true"
33
+ >
34
+ <path d="M3 11.5L12 4l9 7.5" />
35
+ <path d="M5 10.5v9.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-9.5" />
36
+ <path d="M10 21v-5a2 2 0 0 1 4 0v5" />
37
+ </svg>
38
+ Eget SVG-ikon
39
+ </button>
40
+ ```
@@ -0,0 +1,86 @@
1
+ import { render, screen } from '@testing-library/angular'
2
+ import userEvent from '@testing-library/user-event'
3
+ import { vi } from 'vitest'
4
+ import { Button } from './button'
5
+
6
+ it('should render as aria-disabled when aria-disabled is true regardless of variant', async () => {
7
+ await render(
8
+ `
9
+ <button ksd-button aria-disabled="true">My button</button>
10
+ `,
11
+ { imports: [Button] },
12
+ )
13
+
14
+ const button = screen.getByRole('button')
15
+ expect(button).toHaveAttribute('aria-disabled')
16
+ })
17
+
18
+ it('should render as disabled when disabled is true regardless of variant', async () => {
19
+ await render(
20
+ `
21
+ <button ksd-button disabled>My button</button>
22
+ `,
23
+ { imports: [Button] },
24
+ )
25
+
26
+ const button = screen.getByRole('button')
27
+ expect(button).toBeDisabled()
28
+ })
29
+
30
+ it('should be clickable', async () => {
31
+ const handleClick = vi.fn()
32
+
33
+ await render(
34
+ `<button ksd-button (click)="handleClick()">My button</button>`,
35
+ {
36
+ imports: [Button],
37
+ componentProperties: { handleClick },
38
+ },
39
+ )
40
+ const user = userEvent.setup()
41
+ const button = screen.getByRole('button')
42
+
43
+ await user.click(button)
44
+ expect(handleClick).toHaveBeenCalledTimes(1)
45
+ })
46
+
47
+ it('should not be clickable when disabled', async () => {
48
+ const handleClick = vi.fn()
49
+
50
+ await render(
51
+ `<button ksd-button disabled (click)="handleClick()">My button</button>`,
52
+ {
53
+ imports: [Button],
54
+ componentProperties: { handleClick },
55
+ },
56
+ )
57
+ const user = userEvent.setup()
58
+ const button = screen.getByRole('button')
59
+
60
+ await user.click(button)
61
+ expect(handleClick).not.toHaveBeenCalled()
62
+ })
63
+
64
+ it('should render button text', async () => {
65
+ await render(
66
+ `
67
+ <button ksd-button disabled>Different button text</button>
68
+ `,
69
+ { imports: [Button] },
70
+ )
71
+
72
+ const button = screen.getByRole('button', { name: 'Different button text' })
73
+ expect(button).toBeInTheDocument()
74
+ })
75
+
76
+ it('should set aria-busy when loading', async () => {
77
+ await render(
78
+ `
79
+ <button ksd-button loading>My button</button>
80
+ `,
81
+ { imports: [Button] },
82
+ )
83
+
84
+ const button = screen.getByRole('button')
85
+ expect(button).toHaveAttribute('aria-busy', 'true')
86
+ })
@@ -0,0 +1,123 @@
1
+ import { NgIcon, provideIcons } from '@ng-icons/core'
2
+ import { phosphorPencilLine } from '@ng-icons/phosphor-icons/regular'
3
+ import {
4
+ argsToTemplate,
5
+ componentWrapperDecorator,
6
+ moduleMetadata,
7
+ type Meta,
8
+ type StoryObj,
9
+ } from '@storybook/angular'
10
+ import { CommonArgs } from '../../../.storybook/default-args'
11
+ import { Button } from './button'
12
+
13
+ type ButtonArgs = CommonArgs & {
14
+ loading: boolean
15
+ disabled: boolean
16
+ }
17
+
18
+ const meta: Meta<ButtonArgs> = {
19
+ component: Button,
20
+ title: 'Komponenter/Button',
21
+ decorators: [
22
+ moduleMetadata({
23
+ imports: [Button, NgIcon],
24
+ providers: [provideIcons({ phosphorPencilLine })],
25
+ }),
26
+ componentWrapperDecorator(
27
+ (story) =>
28
+ `<div style="display:flex;flex-direction:row;justify-content:center;align-items:center;flex-wrap:wrap;gap:var(--ds-size-4)">${story}</div>`,
29
+ ),
30
+ ],
31
+ }
32
+ export default meta
33
+ type Story = StoryObj<ButtonArgs>
34
+
35
+ export const Preview: Story = {
36
+ args: {
37
+ loading: false,
38
+ disabled: false,
39
+ },
40
+
41
+ render: (args) => ({
42
+ props: args,
43
+ template: `
44
+ <button ksd-button ${argsToTemplate(args)}>
45
+ Knapp
46
+ </button>
47
+ `,
48
+ }),
49
+ }
50
+
51
+ export const Variants: Story = {
52
+ args: {
53
+ ...Preview.args,
54
+ },
55
+ render: (args) => ({
56
+ props: args,
57
+ template: `
58
+ <div style="display:flex;flex-direction:row;justify-content:center;align-items:center;flex-wrap:wrap;gap:var(--ds-size-4);">
59
+ <button ksd-button variant="primary" ${argsToTemplate(args)}>Primary</button>
60
+ <button ksd-button variant="secondary" ${argsToTemplate(args)}>Secondary</button>
61
+ <button ksd-button variant="tertiary" ${argsToTemplate(args)}>Teritiary</button>
62
+ </div>
63
+ `,
64
+ }),
65
+ }
66
+
67
+ export const Icons: Story = {
68
+ args: {
69
+ ...Preview.args,
70
+ },
71
+ render: (args) => ({
72
+ props: args,
73
+ template: `
74
+ <button ksd-button ${argsToTemplate(args)}>
75
+ <ng-icon name="phosphorPencilLine" />
76
+ Rediger
77
+ </button>
78
+
79
+ <button icon ksd-button ${argsToTemplate(args)} aria-label="Kun ikon">
80
+ <ng-icon name="phosphorPencilLine" />
81
+ </button>
82
+
83
+ <button ksd-button ${argsToTemplate(args)}>
84
+ <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" aria-hidden="true">
85
+ <path d="M3 11.5L12 4l9 7.5" />
86
+ <path d="M5 10.5v9.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-9.5" />
87
+ <path d="M10 21v-5a2 2 0 0 1 4 0v5" />
88
+ </svg>
89
+
90
+ Eget SVG-ikon
91
+ </button>
92
+ `,
93
+ }),
94
+ }
95
+
96
+ export const AsLink: Story = {
97
+ args: {
98
+ ...Preview.args,
99
+ },
100
+ render: (args) => ({
101
+ props: args,
102
+ template: `
103
+ <a ksd-button target="_blank" rel="noreferrer" href="https://ksdigital.no" ${argsToTemplate(args)}>Gå til ksdigital.no</a>
104
+ `,
105
+ }),
106
+ }
107
+
108
+ export const Loading: Story = {
109
+ args: {
110
+ ...Preview.args,
111
+ loading: true,
112
+ },
113
+ render: (args) => ({
114
+ props: args,
115
+ template: `
116
+ <div style="display:flex;flex-direction:row;justify-content:center;align-items:center;flex-wrap:wrap;gap:var(--ds-size-4);">
117
+ <button ksd-button variant="primary" ${argsToTemplate(args)}>Primary</button>
118
+ <button ksd-button variant="secondary" ${argsToTemplate(args)}>Secondary</button>
119
+ <button ksd-button variant="tertiary" ${argsToTemplate(args)}>Teritiary</button>
120
+ </div>
121
+ `,
122
+ }),
123
+ }
@@ -0,0 +1,60 @@
1
+ import { booleanAttribute, Component, input } from '@angular/core'
2
+ import { CommonInputs } from '../common-inputs'
3
+ import { Spinner } from '../spinner/spinner'
4
+
5
+ @Component({
6
+ selector: 'button[ksd-button], a[ksd-button]',
7
+ hostDirectives: [
8
+ {
9
+ directive: CommonInputs,
10
+ inputs: ['data-size', 'data-color'],
11
+ },
12
+ ],
13
+ imports: [Spinner],
14
+ host: {
15
+ class: 'ds-button',
16
+ type: 'button',
17
+ '[attr.data-variant]': 'variant()',
18
+ '[attr.data-icon]': 'icon() || null',
19
+ '[attr.disabled]': 'disabled() ? true : null',
20
+ '[attr.aria-busy]': 'loading() ? true : null',
21
+ },
22
+ styles: `
23
+ /* Ensure transcluded icons are aligned properly */
24
+ :host ::ng-deep > * {
25
+ display: inline-flex;
26
+ }
27
+ `,
28
+
29
+ template: `
30
+ @if (loading()) {
31
+ <ksd-spinner aria-hidden="true" />
32
+ }
33
+ <ng-content />
34
+ `,
35
+ })
36
+ export class Button {
37
+ /**
38
+ * Specify which variant to use
39
+ * @default 'primary'
40
+ */
41
+ readonly variant = input<'primary' | 'secondary' | 'tertiary'>('primary')
42
+
43
+ /**
44
+ * Toggle loading state.
45
+ * Pass an element if you want to display a custom loader.
46
+ *
47
+ * @default false
48
+ */
49
+ readonly loading = input(false, { transform: booleanAttribute })
50
+
51
+ /**
52
+ * Disables element
53
+ */
54
+ readonly disabled = input(false, { transform: booleanAttribute })
55
+
56
+ /**
57
+ * If this is a button with only an icon
58
+ */
59
+ readonly icon = input(false, { transform: booleanAttribute })
60
+ }
@@ -0,0 +1 @@
1
+ export { Button } from './button'
@@ -0,0 +1,10 @@
1
+ import { Component } from '@angular/core'
2
+
3
+ @Component({
4
+ selector: '[ksd-card-block]',
5
+ host: {
6
+ class: 'ds-card__block',
7
+ },
8
+ template: `<ng-content />`,
9
+ })
10
+ export class CardBlock {}
@@ -0,0 +1,100 @@
1
+ import {
2
+ Meta,
3
+ Primary,
4
+ Canvas,
5
+ Controls,
6
+ Story,
7
+ } from '@storybook/addon-docs/blocks'
8
+
9
+ import * as CardStories from './card.stories'
10
+
11
+ <Meta of={CardStories} />
12
+
13
+ # Card
14
+
15
+ Med Card kan vi fremheve informasjon eller oppgaver som hører sammen.
16
+
17
+ <Primary />
18
+ <Controls />
19
+
20
+ ## Bruk
21
+
22
+ `ksd-card` brukes som direktiv sammen med riktig HTML-tag i din kontekst, f.eks `article`. Du kan bruke `ksd-card-block` for å dele inn kortet i flere seksjoner.
23
+
24
+ ```html
25
+ <article ksd-card>
26
+ <h2 ksd-card-block>Tittel</h2>
27
+ <p ksd-card-block>Innhold</p>
28
+ <p ksd-card-block>Valgfri fotnote</p>
29
+ </article>
30
+ ```
31
+
32
+ ## Kodeeksempler
33
+
34
+ ### Liste
35
+
36
+ En liste med cards i en grid-layout.
37
+
38
+ ```html
39
+ <ul
40
+ style="display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem;"
41
+ >
42
+ <li ksd-card>
43
+ <h2>Item 1</h2>
44
+ <p>Description</p>
45
+ </li>
46
+ <li ksd-card>
47
+ <h2>Item 2</h2>
48
+ <p>Description</p>
49
+ </li>
50
+ <li ksd-card>
51
+ <h2>Item 3</h2>
52
+ <p>Description</p>
53
+ </li>
54
+ <li ksd-card>
55
+ <h2>Item 4</h2>
56
+ <p>Description</p>
57
+ </li>
58
+ </ul>
59
+ ```
60
+
61
+ ### Navigasjonskort
62
+
63
+ `Card` kan brukes som navigasjonskort for å ta brukeren videre til en annen side.
64
+
65
+ #### Kort med lenke i tittel
66
+
67
+ Dersom kortet skal lenke til en annen side, kan du legge en lenke i kortets tittel. Hele kortet blir da automatisk klikkbart, men skjermlesere får en bedre brukeropplevelse enn dersom hele kortet var en lenke.
68
+ Dette fordi kun lenken istedenfor hele kortets innhold vil bli lest opp ved fokus.
69
+
70
+ ```html
71
+ <article ksd-card>
72
+ <h2><a href="/">Whole card is clickable because of this link</a></h2>
73
+ </article>
74
+ ```
75
+
76
+ #### Kort som er en lenke
77
+
78
+ Hele kortet kan bli en lenke ved å bruke anchor-tag som host-element. Dette er nyttig hvis du ønsker at hele kortets innhold skal bli lest opp.
79
+
80
+ > ⚠ Vær oppmerksom på at dette vanligvis ikke er anbefalt, da det ofte er forstyrrende for skjermleserbrukere hvis kortet har mye innhold.
81
+
82
+ ```html
83
+ <a ksd-card>
84
+ <h2>My heading</h2>
85
+ <p>My paragraph</p>
86
+ </a>
87
+ ```
88
+
89
+ ### Retningslinjer
90
+
91
+ `Card` er en fleksibel komponent som brukes til å strukturere og presentere innhold på en tydelig og visuelt avgrenset måte. Kortet kan inneholde ulike typer innhold, som tekst, media og lenker, og brukes ofte i oversikter, navigasjon eller for å fremheve enkeltemner.
92
+
93
+ #### Passer til:
94
+
95
+ - Å gruppere innhold eller funksjonalitet som du vil skille ut fra resten av innholdet.
96
+
97
+ #### Passer ikke til:
98
+
99
+ - Å vise lange tekstblokker eller detaljerte forklaringer.
100
+ - Viktige meldinger som bør presenteres som varsler (bruk heller Alert).
@@ -0,0 +1,70 @@
1
+ import { fireEvent, render, screen } from '@testing-library/angular'
2
+ import userEvent from '@testing-library/user-event'
3
+ import { vi } from 'vitest'
4
+ import { Card } from './card'
5
+
6
+ it('should render card', async () => {
7
+ await render(
8
+ `
9
+ <article ksd-card>My card</article>
10
+ `,
11
+ { imports: [Card] },
12
+ )
13
+
14
+ const card = screen.getByRole('article')
15
+ expect(card).toHaveClass('ds-card')
16
+ })
17
+
18
+ it('clicking anywhere on the card triggers the inner link', async () => {
19
+ await render(
20
+ `
21
+ <article ksd-card>
22
+ <h2><a href="https://vg.no">My link</a></h2>
23
+ <p>My paragraph</p>
24
+ </article>
25
+ `,
26
+ { imports: [Card] },
27
+ )
28
+
29
+ const user = userEvent.setup()
30
+ const card = screen.getByRole('article')
31
+
32
+ // Spy on the link inside the card
33
+ const clickSpy = vi
34
+ .spyOn(HTMLAnchorElement.prototype, 'click')
35
+ .mockImplementation(() => undefined)
36
+
37
+ await user.click(card)
38
+
39
+ // Expect that the link inside the card has been clicked
40
+ expect(clickSpy).toHaveBeenCalledTimes(1)
41
+ clickSpy.mockRestore()
42
+ })
43
+
44
+ it('opens link in new tab with noopener,noreferrer on meta/ctrl click', async () => {
45
+ await render(
46
+ `
47
+ <article ksd-card>
48
+ <h2><a href="https://vg.no">My link</a></h2>
49
+ <p>My paragraph</p>
50
+ </article>
51
+ `,
52
+ { imports: [Card] },
53
+ )
54
+
55
+ const card = screen.getByRole('article')
56
+ const anchor = screen.getByRole('link') as HTMLAnchorElement
57
+
58
+ const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null)
59
+
60
+ fireEvent.click(card, { ctrlKey: true })
61
+
62
+ expect(openSpy).toHaveBeenCalledTimes(1)
63
+ expect(openSpy).toHaveBeenCalledWith(
64
+ anchor.href,
65
+ '_blank',
66
+ 'noopener,noreferrer',
67
+ )
68
+
69
+ openSpy.mockRestore()
70
+ })
@@ -0,0 +1,101 @@
1
+ import {
2
+ argsToTemplate,
3
+ moduleMetadata,
4
+ type Meta,
5
+ type StoryObj,
6
+ } from '@storybook/angular'
7
+ import { CommonArgs, commonArgTypes } from '../../../.storybook/default-args'
8
+ import { Card } from './card'
9
+ import { CardBlock } from './card-block'
10
+
11
+ type CardArgs = CommonArgs & {
12
+ variant: 'default' | 'tinted'
13
+ }
14
+
15
+ const meta: Meta<CardArgs> = {
16
+ component: Card,
17
+ title: 'Komponenter/Card',
18
+ argTypes: {
19
+ ...commonArgTypes,
20
+ variant: {
21
+ options: ['default', 'tinted'],
22
+ control: { type: 'radio' },
23
+ },
24
+ },
25
+ decorators: [
26
+ moduleMetadata({
27
+ imports: [Card, CardBlock],
28
+ }),
29
+ ],
30
+ }
31
+ export default meta
32
+ type Story = StoryObj<CardArgs>
33
+
34
+ export const Preview: Story = {
35
+ render: (args) => ({
36
+ props: args,
37
+ template: `
38
+ <div style="max-width: 320px;">
39
+ <article ksd-card ${argsToTemplate(args)}>
40
+ <h2>Card</h2>
41
+ <p>Most provide as with carried business are much better more the perfected designer. Writing slightly explain desk unable at supposedly about this</p>
42
+ <p data-size="sm">Footer text</p>
43
+ </article>
44
+ </div>
45
+ `,
46
+ }),
47
+ }
48
+
49
+ export const CardWithBlocks: Story = {
50
+ render: (args) => ({
51
+ props: args,
52
+ template: `
53
+ <div style="max-width: 320px;">
54
+ <article ksd-card ${argsToTemplate(args)}>
55
+ <h2 ksd-card-block>Use blocks to section the card</h2>
56
+ <p ksd-card-block>Most provide as with carried business are much better more the perfected designer. Writing slightly explain desk unable at supposedly about this</p>
57
+ <p ksd-card-block>Valgfri fotnote</p>
58
+ </article>
59
+ </div>
60
+ `,
61
+ }),
62
+ }
63
+
64
+ export const ListOfCards: Story = {
65
+ render: (args) => ({
66
+ props: args,
67
+ template: `
68
+ <ul style="display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem;">
69
+ <li ksd-card ${argsToTemplate(args)}>
70
+ <h2>Item 1</h2>
71
+ <p>Description</p>
72
+ </li>
73
+ <li ksd-card ${argsToTemplate(args)}>
74
+ <h2>Item 2</h2>
75
+ <p>Description</p>
76
+ </li>
77
+ <li ksd-card ${argsToTemplate(args)}>
78
+ <h2>Item 3</h2>
79
+ <p>Description</p>
80
+ </li>
81
+ <li ksd-card ${argsToTemplate(args)}>
82
+ <h2>Item 4</h2>
83
+ <p>Description</p>
84
+ </li>
85
+ </ul>
86
+ `,
87
+ }),
88
+ }
89
+
90
+ export const AsLink: Story = {
91
+ render: (args) => ({
92
+ props: args,
93
+ template: `
94
+ <div style="max-width: 320px;">
95
+ <article ksd-card ${argsToTemplate(args)}>
96
+ <h2><a href="/">Whole card is clickable when link is present inside heading</a></h2>
97
+ </article>
98
+ </div>
99
+ `,
100
+ }),
101
+ }
@@ -0,0 +1,44 @@
1
+ import { Component, ElementRef, inject, input } from '@angular/core'
2
+ import { CommonInputs } from '../common-inputs'
3
+
4
+ @Component({
5
+ selector: '[ksd-card]',
6
+ template: ` <ng-content /> `,
7
+ hostDirectives: [
8
+ {
9
+ directive: CommonInputs,
10
+ inputs: ['data-size', 'data-color'],
11
+ },
12
+ ],
13
+ host: {
14
+ class: 'ds-card',
15
+ '[attr.data-variant]': 'variant()',
16
+ '(click)': 'handleClick($event)',
17
+ },
18
+ })
19
+ export class Card {
20
+ /**
21
+ * Change the background color of the card
22
+ * @default 'default'
23
+ */
24
+ public variant = input<'tinted' | 'default'>('default')
25
+ private elementRef = inject(ElementRef)
26
+
27
+ private projectedLink() {
28
+ const el = this.elementRef.nativeElement
29
+ return el?.querySelector(
30
+ 'h1 a, h2 a, h3 a, h4 a, h5 a, h6 a',
31
+ ) as HTMLAnchorElement | null
32
+ }
33
+
34
+ protected handleClick = (event: MouseEvent) => {
35
+ const link = this.projectedLink()
36
+ if (!link) return
37
+
38
+ if (event.metaKey || event.ctrlKey) {
39
+ window.open(link.href, '_blank', 'noopener,noreferrer')
40
+ } else {
41
+ link.click()
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,2 @@
1
+ export { Card } from './card'
2
+ export { CardBlock } from './card-block'
@@ -0,0 +1,13 @@
1
+ # Checkbox
2
+
3
+ # Todo
4
+
5
+ - [x] Basic checkbox
6
+ - [x] Group
7
+ - [x] Aria-label
8
+ - [x] Description
9
+ - [x] Read only
10
+ - [x] Disabeled
11
+ - [ ] In table
12
+ - [ ] Conditional
13
+ - [ ] In table with pagination