fds-vue-core 2.1.4 → 2.1.6

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 (121) hide show
  1. package/components.d.ts +8 -0
  2. package/configs/tsconfig.base.json +2 -1
  3. package/dist/fds-vue-core.cjs.js +35 -15
  4. package/dist/fds-vue-core.cjs.js.map +1 -1
  5. package/dist/fds-vue-core.es.js +35 -15
  6. package/dist/fds-vue-core.es.js.map +1 -1
  7. package/dist/global-components.d.ts +35 -33
  8. package/package.json +23 -21
  9. package/src/.DS_Store +0 -0
  10. package/src/App.vue +133 -0
  11. package/src/apply.css +60 -0
  12. package/src/assets/icons.ts +517 -0
  13. package/src/components/Blocks/FdsBlockAlert/FdsBlockAlert.stories.ts +94 -0
  14. package/src/components/Blocks/FdsBlockAlert/FdsBlockAlert.vue +112 -0
  15. package/src/components/Blocks/FdsBlockAlert/types.ts +12 -0
  16. package/src/components/Blocks/FdsBlockContent/FdsBlockContent.stories.ts +110 -0
  17. package/src/components/Blocks/FdsBlockContent/FdsBlockContent.vue +66 -0
  18. package/src/components/Blocks/FdsBlockContent/types.ts +6 -0
  19. package/src/components/Blocks/FdsBlockExpander/FdsBlockExpander.stories.ts +123 -0
  20. package/src/components/Blocks/FdsBlockExpander/FdsBlockExpander.vue +87 -0
  21. package/src/components/Blocks/FdsBlockExpander/types.ts +8 -0
  22. package/src/components/Blocks/FdsBlockInfo/FdsBlockInfo.stories.ts +110 -0
  23. package/src/components/Blocks/FdsBlockInfo/FdsBlockInfo.vue +75 -0
  24. package/src/components/Blocks/FdsBlockInfo/types.ts +9 -0
  25. package/src/components/Blocks/FdsBlockLink/FdsBlockLink.css +9 -0
  26. package/src/components/Blocks/FdsBlockLink/FdsBlockLink.stories.ts +179 -0
  27. package/src/components/Blocks/FdsBlockLink/FdsBlockLink.vue +149 -0
  28. package/src/components/Blocks/FdsBlockLink/types.ts +14 -0
  29. package/src/components/Buttons/ButtonBaseProps.ts +18 -0
  30. package/src/components/Buttons/FdsButtonCopy/FdsButtonCopy.stories.ts +53 -0
  31. package/src/components/Buttons/FdsButtonCopy/FdsButtonCopy.vue +87 -0
  32. package/src/components/Buttons/FdsButtonCopy/types.ts +8 -0
  33. package/src/components/Buttons/FdsButtonDownload/FdsButtonDownload.stories.ts +111 -0
  34. package/src/components/Buttons/FdsButtonDownload/FdsButtonDownload.vue +187 -0
  35. package/src/components/Buttons/FdsButtonIcon/FdsButtonIcon.stories.ts +55 -0
  36. package/src/components/Buttons/FdsButtonIcon/FdsButtonIcon.vue +57 -0
  37. package/src/components/Buttons/FdsButtonIcon/types.ts +12 -0
  38. package/src/components/Buttons/FdsButtonMinor/FdsButtonMinor.stories.ts +68 -0
  39. package/src/components/Buttons/FdsButtonMinor/FdsButtonMinor.vue +126 -0
  40. package/src/components/Buttons/FdsButtonPrimary/FdsButtonPrimary.stories.ts +86 -0
  41. package/src/components/Buttons/FdsButtonPrimary/FdsButtonPrimary.vue +107 -0
  42. package/src/components/Buttons/FdsButtonSecondary/FdsButtonSecondary.stories.ts +68 -0
  43. package/src/components/Buttons/FdsButtonSecondary/FdsButtonSecondary.vue +107 -0
  44. package/src/components/FdsIcon/FdsIcon.stories.ts +69 -0
  45. package/src/components/FdsIcon/FdsIcon.vue +34 -0
  46. package/src/components/FdsIcon/types.ts +9 -0
  47. package/src/components/FdsModal/FdsModal.stories.ts +241 -0
  48. package/src/components/FdsModal/FdsModal.vue +269 -0
  49. package/src/components/FdsModal/types.ts +12 -0
  50. package/src/components/FdsPagination/FdsPagination.stories.ts +109 -0
  51. package/src/components/FdsPagination/FdsPagination.vue +193 -0
  52. package/src/components/FdsPagination/types.ts +6 -0
  53. package/src/components/FdsSearchSelect/FdsSearchSelect.stories.ts +428 -0
  54. package/src/components/FdsSearchSelect/FdsSearchSelect.vue +621 -0
  55. package/src/components/FdsSearchSelect/types.ts +25 -0
  56. package/src/components/FdsSpinner/FdsSpinner.stories.ts +31 -0
  57. package/src/components/FdsSpinner/FdsSpinner.vue +90 -0
  58. package/src/components/FdsSpinner/types.ts +6 -0
  59. package/src/components/FdsSticker/FdsSticker.stories.ts +148 -0
  60. package/src/components/FdsSticker/FdsSticker.vue +44 -0
  61. package/src/components/FdsSticker/types.ts +4 -0
  62. package/src/components/FdsTreeView/FdsTreeView.stories.ts +136 -0
  63. package/src/components/FdsTreeView/FdsTreeView.vue +162 -0
  64. package/src/components/FdsTreeView/TreeNode.vue +383 -0
  65. package/src/components/FdsTreeView/types.ts +141 -0
  66. package/src/components/FdsTreeView/useTreeState.ts +607 -0
  67. package/src/components/FdsTreeView/utils.ts +69 -0
  68. package/src/components/FdsTruncatedText/FdsTruncatedText.stories.ts +78 -0
  69. package/src/components/FdsTruncatedText/FdsTruncatedText.vue +85 -0
  70. package/src/components/FdsTruncatedText/types.ts +6 -0
  71. package/src/components/Form/FdsCheckbox/FdsCheckbox.stories.ts +275 -0
  72. package/src/components/Form/FdsCheckbox/FdsCheckbox.vue +155 -0
  73. package/src/components/Form/FdsCheckbox/types.ts +10 -0
  74. package/src/components/Form/FdsInput/FdsInput.stories.ts +319 -0
  75. package/src/components/Form/FdsInput/FdsInput.vue +233 -0
  76. package/src/components/Form/FdsInput/types.ts +25 -0
  77. package/src/components/Form/FdsRadio/FdsRadio.stories.ts +63 -0
  78. package/src/components/Form/FdsRadio/FdsRadio.vue +88 -0
  79. package/src/components/Form/FdsRadio/types.ts +12 -0
  80. package/src/components/Form/FdsSelect/FdsSelect.stories.ts +78 -0
  81. package/src/components/Form/FdsSelect/FdsSelect.vue +136 -0
  82. package/src/components/Form/FdsSelect/types.ts +13 -0
  83. package/src/components/Form/FdsTextarea/FdsTextarea.stories.ts +52 -0
  84. package/src/components/Form/FdsTextarea/FdsTextarea.vue +110 -0
  85. package/src/components/Form/FdsTextarea/types.ts +12 -0
  86. package/src/components/Table/FdsTable/FdsTable.stories.ts +221 -0
  87. package/src/components/Table/FdsTable/FdsTable.vue +25 -0
  88. package/src/components/Table/FdsTable/types.ts +4 -0
  89. package/src/components/Table/FdsTableHead/FdsTableHead.stories.ts +151 -0
  90. package/src/components/Table/FdsTableHead/FdsTableHead.vue +54 -0
  91. package/src/components/Table/FdsTableHead/types.ts +5 -0
  92. package/src/components/Tabs/FdsTabs/FdsTabs.stories.ts +247 -0
  93. package/src/components/Tabs/FdsTabs/FdsTabs.vue +27 -0
  94. package/src/components/Tabs/FdsTabs/types.ts +4 -0
  95. package/src/components/Tabs/FdsTabsItem/FdsTabsItem.vue +125 -0
  96. package/src/components/Tabs/FdsTabsItem/types.ts +16 -0
  97. package/src/components/Typography/FdsHeading/FdsHeading.stories.ts +93 -0
  98. package/src/components/Typography/FdsHeading/FdsHeading.vue +51 -0
  99. package/src/components/Typography/FdsHeading/types.ts +5 -0
  100. package/src/components/Typography/FdsListHeading/FdsListHeading.stories.ts +58 -0
  101. package/src/components/Typography/FdsListHeading/FdsListHeading.vue +62 -0
  102. package/src/components/Typography/FdsListHeading/types.ts +8 -0
  103. package/src/components/Typography/FdsSeparator/FdsSeparator.stories.ts +31 -0
  104. package/src/components/Typography/FdsSeparator/FdsSeparator.vue +5 -0
  105. package/src/components/Typography/FdsText/FdsText.stories.ts +66 -0
  106. package/src/components/Typography/FdsText/FdsText.vue +28 -0
  107. package/src/components/Typography/FdsText/types.ts +3 -0
  108. package/src/composables/useBoldQuery.ts +29 -0
  109. package/src/composables/useElementFinalSize.ts +24 -0
  110. package/src/composables/useHasSlots.ts +17 -0
  111. package/src/composables/useIsPid.ts +48 -0
  112. package/src/docs/Start/Start.mdx +12 -0
  113. package/src/docs/Usage.md +117 -0
  114. package/src/fonts.css +28 -0
  115. package/src/global-components.ts +75 -0
  116. package/src/index.ts +180 -0
  117. package/src/main.ts +7 -0
  118. package/src/slot-styles.css +93 -0
  119. package/src/style.css +89 -0
  120. package/src/tokens.css +252 -0
  121. package/dist/index.d.ts +0 -2
@@ -0,0 +1,193 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref, watch } from 'vue'
3
+ import FdsButtonIcon from '@/components/Buttons/FdsButtonIcon/FdsButtonIcon.vue'
4
+ import FdsSpinner from '@/components/FdsSpinner/FdsSpinner.vue'
5
+ import FdsInput from '@/components/Form/FdsInput/FdsInput.vue'
6
+ import type { FdsPaginationProps } from './types'
7
+
8
+ const props = withDefaults(defineProps<FdsPaginationProps>(), {
9
+ id: undefined,
10
+ loading: false,
11
+ })
12
+
13
+ const emit = defineEmits<{
14
+ (e: 'paginate', payload: { target: { id: string }; detail: number }): void
15
+ }>()
16
+
17
+ const loadingIndicator = ref<'start' | 'prev' | 'input' | 'next' | 'end' | ''>('')
18
+ const internalValue = ref(props.current)
19
+
20
+ const autoId = `fds-pagination-${Math.random().toString(36).slice(2, 9)}`
21
+ const inputId = computed(() => props.id ?? autoId)
22
+
23
+ const inputValue = computed(() => String(internalValue.value))
24
+
25
+ watch(
26
+ () => props.current,
27
+ (newValue) => {
28
+ internalValue.value = newValue
29
+ },
30
+ )
31
+
32
+ const debounce = <T extends (...args: any[]) => void>(fn: T, delay = 300) => {
33
+ let timer: ReturnType<typeof setTimeout>
34
+ return (...args: Parameters<T>) => {
35
+ clearTimeout(timer)
36
+ timer = setTimeout(() => {
37
+ fn(...args)
38
+ }, delay)
39
+ }
40
+ }
41
+
42
+ const handlePageChange = debounce((event: Event) => {
43
+ const target = event.target as HTMLInputElement
44
+ const incomingPage = Number.parseInt(target.value)
45
+
46
+ if (Number.isNaN(incomingPage)) {
47
+ internalValue.value = props.current
48
+ return
49
+ }
50
+
51
+ loadingIndicator.value = 'input'
52
+
53
+ let page = props.current
54
+ if (incomingPage > props.max) {
55
+ page = props.max
56
+ } else if (incomingPage < 1) {
57
+ page = 1
58
+ } else {
59
+ page = incomingPage
60
+ }
61
+
62
+ internalValue.value = page
63
+ emit('paginate', { target: { id: inputId.value }, detail: page })
64
+ }, 500)
65
+
66
+ const handlePagination = (action: 'start' | 'prev' | 'next' | 'end') => {
67
+ let page = 0
68
+
69
+ switch (action) {
70
+ case 'start':
71
+ if (props.current === 1) return
72
+ loadingIndicator.value = 'start'
73
+ page = 1
74
+ break
75
+ case 'prev':
76
+ if (props.current === 1) return
77
+ loadingIndicator.value = 'prev'
78
+ page = props.current === 1 ? props.current : props.current - 1
79
+ break
80
+ case 'next':
81
+ if (props.current === props.max) return
82
+ loadingIndicator.value = 'next'
83
+ page = props.current === props.max ? props.current : props.current + 1
84
+ break
85
+ case 'end':
86
+ if (props.current === props.max) return
87
+ loadingIndicator.value = 'end'
88
+ page = props.max
89
+ break
90
+ }
91
+
92
+ emit('paginate', { target: { id: inputId.value }, detail: page })
93
+ }
94
+ </script>
95
+
96
+ <template>
97
+ <div
98
+ class="my-6 flex items-between justify-between gap-2"
99
+ :id="inputId"
100
+ >
101
+ <!-- Prev -->
102
+ <div class="flex items-center justify-start gap-1 w-[100px]">
103
+ <FdsSpinner
104
+ v-if="loading && loadingIndicator === 'start'"
105
+ size="32px"
106
+ color="blue"
107
+ />
108
+ <FdsButtonIcon
109
+ v-else
110
+ icon="first"
111
+ :size="28"
112
+ :disabled="current === 1"
113
+ :ariaDisabled="current === 1"
114
+ :class="{ 'hidden!': current === 1 }"
115
+ class="w-8 h-8 sm:w-12 sm:h-12"
116
+ @click="handlePagination('start')"
117
+ />
118
+
119
+ <FdsSpinner
120
+ v-if="loading && loadingIndicator === 'prev'"
121
+ size="24px"
122
+ color="blue"
123
+ />
124
+ <FdsButtonIcon
125
+ v-else
126
+ icon="arrowLeft"
127
+ :size="28"
128
+ :disabled="current === 1"
129
+ :ariaDisabled="current === 1"
130
+ :class="{ 'hidden!': current === 1 }"
131
+ class="w-8 h-8 sm:w-12 sm:h-12"
132
+ @click="handlePagination('prev')"
133
+ />
134
+ </div>
135
+
136
+ <!-- Numbers -->
137
+ <div class="flex items-center justify-center w-auto gap-2 order-0 sm:order-0">
138
+ <FdsSpinner
139
+ v-if="loading && loadingIndicator === 'input'"
140
+ size="24px"
141
+ color="blue"
142
+ label="Laddar"
143
+ label-position="right"
144
+ />
145
+ <template v-else>
146
+ <FdsInput
147
+ :value="inputValue"
148
+ type="text"
149
+ :size="max.toString().length"
150
+ :maxlength="max.toString().length"
151
+ @input="handlePageChange"
152
+ class="mb-0! text-center"
153
+ />
154
+ <div class="whitespace-nowrap">/ {{ max }}</div>
155
+ </template>
156
+ </div>
157
+
158
+ <!-- Next -->
159
+ <div class="flex items-center justify-end gap-1 order-2 sm:order-0 w-[100px]">
160
+ <FdsSpinner
161
+ v-if="loading && loadingIndicator === 'next'"
162
+ size="24px"
163
+ color="blue"
164
+ />
165
+ <FdsButtonIcon
166
+ v-else
167
+ icon="arrowRight"
168
+ :size="28"
169
+ :disabled="current === max"
170
+ :ariaDisabled="current === max"
171
+ :class="{ 'hidden!': current === max }"
172
+ class="w-8 h-8 sm:w-12 sm:h-12"
173
+ @click="handlePagination('next')"
174
+ />
175
+
176
+ <FdsSpinner
177
+ v-if="loading && loadingIndicator === 'end'"
178
+ size="24px"
179
+ color="blue"
180
+ />
181
+ <FdsButtonIcon
182
+ v-else
183
+ icon="last"
184
+ :size="28"
185
+ :disabled="current === max"
186
+ :ariaDisabled="current === max"
187
+ :class="{ 'hidden!': current === max }"
188
+ class="w-8 h-8 sm:w-12 sm:h-12"
189
+ @click="handlePagination('end')"
190
+ />
191
+ </div>
192
+ </div>
193
+ </template>
@@ -0,0 +1,6 @@
1
+ export interface FdsPaginationProps {
2
+ id?: string
3
+ current: number
4
+ max: number
5
+ loading?: boolean
6
+ }
@@ -0,0 +1,428 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import { ref } from 'vue'
3
+ import FdsSearchSelect from './FdsSearchSelect.vue'
4
+
5
+ const meta: Meta<typeof FdsSearchSelect> = {
6
+ title: 'FDS/Form/FdsSearchSelect',
7
+ component: FdsSearchSelect,
8
+ tags: ['autodocs'],
9
+ argTypes: {
10
+ items: {
11
+ control: { type: 'object' },
12
+ description: 'Array of items to display in the search select dropdown',
13
+ },
14
+ page: {
15
+ control: { type: 'number' },
16
+ description: 'Current page number for server-side pagination',
17
+ },
18
+ totalPages: {
19
+ control: { type: 'number' },
20
+ description: 'Total number of pages for server-side pagination',
21
+ },
22
+ totalCount: {
23
+ control: { type: 'number' },
24
+ description: 'Total count of unfiltered items (displayed as "x av y")',
25
+ },
26
+ loading: {
27
+ control: { type: 'boolean' },
28
+ description: 'Shows loading spinner in the dropdown when true',
29
+ },
30
+ searchFields: {
31
+ control: { type: 'object' },
32
+ description: 'Array of field names to search in (e.g., ["fullName", "email"])',
33
+ },
34
+ preserveOrder: {
35
+ control: { type: 'boolean' },
36
+ description: 'If true, preserves the order of searchFields when displaying results',
37
+ },
38
+ initialValue: {
39
+ control: { type: 'text' },
40
+ description: 'Initial value to display in the input field',
41
+ },
42
+ disabled: {
43
+ control: { type: 'boolean' },
44
+ description: 'Disables the search select component',
45
+ },
46
+ dropdownAbsolute: {
47
+ control: { type: 'boolean' },
48
+ description: 'If true, positions the dropdown absolutely',
49
+ },
50
+ labelLeft: {
51
+ control: { type: 'boolean' },
52
+ description: 'If true, positions the label to the left of the input',
53
+ },
54
+ label: {
55
+ control: { type: 'text' },
56
+ description: 'Label text for the input field',
57
+ },
58
+ meta: {
59
+ control: { type: 'text' },
60
+ description: 'Meta text displayed below the label',
61
+ },
62
+ singleItemLabel: {
63
+ control: { type: 'text' },
64
+ description: 'Label text displayed when a single item is shown',
65
+ },
66
+ searchContext: {
67
+ control: { type: 'object' },
68
+ description: 'Context object with "context" and "linkWord" for displaying search results count',
69
+ },
70
+ valid: {
71
+ control: { type: 'select' },
72
+ options: ['true', 'false', 'null'],
73
+ description: 'Validation state: "true" for valid, "false" for invalid, "null" for neutral',
74
+ },
75
+ invalidMessage: {
76
+ control: { type: 'text' },
77
+ description: 'Error message displayed when validation is false',
78
+ },
79
+ noResultPrompt: {
80
+ control: { type: 'text' },
81
+ description: 'Message displayed when no search results are found',
82
+ },
83
+ borderless: {
84
+ control: { type: 'boolean' },
85
+ description: 'If true, removes borders from the dropdown list',
86
+ },
87
+ marginless: {
88
+ control: { type: 'boolean' },
89
+ description: 'If true, removes bottom margin from the component',
90
+ },
91
+ maxListHeight: {
92
+ control: { type: 'number' },
93
+ description: 'Maximum height of the dropdown list in pixels',
94
+ },
95
+ locale: {
96
+ control: { type: 'select' },
97
+ options: ['sv', 'en'],
98
+ description: 'Locale for translations ("sv" for Swedish, "en" for English)',
99
+ },
100
+ clearTrigger: {
101
+ control: { type: 'boolean' },
102
+ description: 'When set to true, clears the search input and selection',
103
+ },
104
+ },
105
+ args: {
106
+ items: [],
107
+ searchFields: ['companyName', 'orgNumber'],
108
+ preserveOrder: false,
109
+ initialValue: '',
110
+ disabled: false,
111
+ dropdownAbsolute: false,
112
+ labelLeft: false,
113
+ label: 'Sök organisation',
114
+ meta: undefined,
115
+ singleItemLabel: 'Vald organisation',
116
+ searchContext: { context: 'organisationer', linkWord: 'av' },
117
+ valid: undefined,
118
+ invalidMessage: 'Vänligen välj en organisation',
119
+ noResultPrompt: 'Inga resultat hittades',
120
+ borderless: false,
121
+ marginless: false,
122
+ maxListHeight: undefined,
123
+ locale: 'sv',
124
+ clearTrigger: false,
125
+ },
126
+ }
127
+
128
+ export default meta
129
+ type Story = StoryObj<typeof meta>
130
+
131
+ // Mock data for items examples
132
+ const mockItems = [
133
+ {
134
+ companyName: 'Acme Corporation',
135
+ orgNumber: '1234567890',
136
+ companyFqn: 'Acme Corporation | 1234567890',
137
+ },
138
+ {
139
+ companyName: 'Tech Solutions AB',
140
+ orgNumber: '9876543210',
141
+ companyFqn: 'Tech Solutions AB | 9876543210',
142
+ },
143
+ {
144
+ companyName: 'Global Industries',
145
+ orgNumber: '5555555555',
146
+ companyFqn: 'Global Industries | 5555555555',
147
+ },
148
+ {
149
+ companyName: 'Nordic Ventures',
150
+ orgNumber: '1111111111',
151
+ companyFqn: 'Nordic Ventures | 1111111111',
152
+ },
153
+ {
154
+ companyName: 'Scandinavian Group',
155
+ orgNumber: '2222222222',
156
+ companyFqn: 'Scandinavian Group | 2222222222',
157
+ },
158
+ ]
159
+
160
+ export const WithItems: Story = {
161
+ render: (args) => ({
162
+ components: { FdsSearchSelect },
163
+ setup: () => {
164
+ const selected = ref<Record<string, unknown> | null>(null)
165
+ const handleSearchSelected = (value: Record<string, unknown> | null) => {
166
+ selected.value = value
167
+ }
168
+ return { args, selected, handleSearchSelected }
169
+ },
170
+ template: `
171
+ <FdsSearchSelect
172
+ v-bind="args"
173
+ :items="args.items"
174
+ @searchSelected="handleSearchSelected"
175
+ />
176
+
177
+ `,
178
+ }),
179
+ args: {
180
+ items: mockItems,
181
+ label: 'Sök bland organisationer',
182
+ meta: 'Börja skriva för att söka',
183
+ },
184
+ }
185
+
186
+ export const WithSelectedEarlier: Story = {
187
+ render: (args) => ({
188
+ components: { FdsSearchSelect },
189
+ setup: () => {
190
+ const selected = ref<Record<string, unknown> | null>(null)
191
+ const handleSearchSelected = (value: Record<string, unknown> | null) => {
192
+ selected.value = value
193
+ }
194
+ return { args, selected, handleSearchSelected }
195
+ },
196
+ template: `
197
+ <FdsSearchSelect
198
+ v-bind="args"
199
+ :items="args.items"
200
+ @searchSelected="handleSearchSelected"
201
+ />
202
+ `,
203
+ }),
204
+ args: {
205
+ items: mockItems,
206
+ initialValue: 'Acme Corporation',
207
+ label: 'Organisation',
208
+ },
209
+ }
210
+
211
+ export const SingleOrganization: Story = {
212
+ render: (args) => ({
213
+ components: { FdsSearchSelect },
214
+ setup: () => {
215
+ const selected = ref<Record<string, unknown> | null>(null)
216
+ const handleSearchSelected = (value: Record<string, unknown> | null) => {
217
+ selected.value = value
218
+ }
219
+ return { args, selected, handleSearchSelected }
220
+ },
221
+ template: `
222
+ <FdsSearchSelect
223
+ v-bind="args"
224
+ :items="args.items"
225
+ @searchSelected="handleSearchSelected"
226
+ />
227
+ `,
228
+ }),
229
+ args: {
230
+ items: [
231
+ {
232
+ companyName: 'Acme Corporation',
233
+ orgNumber: '1234567890',
234
+ companyFqn: 'Acme Corporation | 1234567890',
235
+ },
236
+ ],
237
+ singleItemLabel: 'Vald organisation',
238
+ label: 'Organisation',
239
+ },
240
+ }
241
+
242
+ export const WithValidation: Story = {
243
+ render: (args) => ({
244
+ components: { FdsSearchSelect },
245
+ setup: () => {
246
+ const selected = ref<Record<string, unknown> | null>(null)
247
+ const valid = ref<string | undefined>('null')
248
+ const handleSearchSelected = (value: Record<string, unknown> | null) => {
249
+ selected.value = value
250
+ valid.value = value ? 'true' : 'null'
251
+ }
252
+ return { args, selected, valid, handleSearchSelected }
253
+ },
254
+ template: `
255
+ <FdsSearchSelect
256
+ v-bind="args"
257
+ :items="args.items"
258
+ :valid="valid"
259
+ @searchSelected="handleSearchSelected"
260
+ />
261
+ `,
262
+ }),
263
+ args: {
264
+ items: mockItems,
265
+ label: 'Välj organisation',
266
+ invalidMessage: 'Vänligen välj en organisation',
267
+ },
268
+ }
269
+
270
+ export const WithLabelLeft: Story = {
271
+ render: (args) => ({
272
+ components: { FdsSearchSelect },
273
+ setup: () => {
274
+ const selected = ref<Record<string, unknown> | null>(null)
275
+ const handleSearchSelected = (value: Record<string, unknown> | null) => {
276
+ selected.value = value
277
+ }
278
+ return { args, selected, handleSearchSelected }
279
+ },
280
+ template: `
281
+ <div>
282
+ <FdsSearchSelect
283
+ v-bind="args"
284
+ :items="args.items"
285
+ @searchSelected="handleSearchSelected"
286
+ />
287
+ </div>
288
+ `,
289
+ }),
290
+ args: {
291
+ items: mockItems,
292
+ label: 'Organisation',
293
+ labelLeft: true,
294
+ meta: 'Sök efter organisation',
295
+ },
296
+ }
297
+
298
+ export const WithAbsoluteList: Story = {
299
+ render: (args) => ({
300
+ components: { FdsSearchSelect },
301
+ setup: () => {
302
+ const selected = ref<Record<string, unknown> | null>(null)
303
+ const handleSearchSelected = (value: Record<string, unknown> | null) => {
304
+ selected.value = value
305
+ }
306
+ return { args, selected, handleSearchSelected }
307
+ },
308
+ template: `
309
+ <div style="position: relative; padding: 2rem;">
310
+ <FdsSearchSelect
311
+ v-bind="args"
312
+ :items="args.items"
313
+ @searchSelected="handleSearchSelected"
314
+ />
315
+ </div>
316
+ `,
317
+ }),
318
+ args: {
319
+ items: mockItems,
320
+ dropdownAbsolute: true,
321
+ label: 'Sök organisation',
322
+ },
323
+ }
324
+
325
+ export const Disabled: Story = {
326
+ render: (args) => ({
327
+ components: { FdsSearchSelect },
328
+ setup: () => ({ args }),
329
+ template: '<FdsSearchSelect v-bind="args" :items="args.items" />',
330
+ }),
331
+ args: {
332
+ items: mockItems,
333
+ disabled: true,
334
+ label: 'Sök organisation',
335
+ },
336
+ }
337
+
338
+ export const EnglishLocale: Story = {
339
+ render: (args) => ({
340
+ components: { FdsSearchSelect },
341
+ setup: () => {
342
+ const selected = ref<Record<string, unknown> | null>(null)
343
+ const handleSearchSelected = (value: Record<string, unknown> | null) => {
344
+ selected.value = value
345
+ }
346
+ return { args, selected, handleSearchSelected }
347
+ },
348
+ template: `
349
+ <div>
350
+ <FdsSearchSelect
351
+ v-bind="args"
352
+ :items="args.items"
353
+ @searchSelected="handleSearchSelected"
354
+ />
355
+ </div>
356
+ `,
357
+ }),
358
+ args: {
359
+ items: mockItems,
360
+ locale: 'en',
361
+ label: 'Search organization',
362
+ meta: 'Start typing to search',
363
+ singleItemLabel: 'Selected organization',
364
+ searchContext: { context: 'organizations', linkWord: 'of' },
365
+ noResultPrompt: 'No results found',
366
+ invalidMessage: 'Please select an organization',
367
+ },
368
+ }
369
+
370
+ export const WithPagination: Story = {
371
+ render: (args) => ({
372
+ components: { FdsSearchSelect },
373
+ setup: () => {
374
+ const selected = ref<Record<string, unknown> | null>(null)
375
+ const handleSearchSelected = (value: Record<string, unknown> | null) => {
376
+ selected.value = value
377
+ }
378
+ const handlePaginate = (page: number) => {
379
+ // eslint-disable-next-line no-console
380
+ console.log('Paginate to page:', page)
381
+ }
382
+ return { args, selected, handleSearchSelected, handlePaginate }
383
+ },
384
+ template: `
385
+ <div>
386
+ <FdsSearchSelect
387
+ v-bind="args"
388
+ :items="args.items"
389
+ @searchSelected="handleSearchSelected"
390
+ @paginate="handlePaginate"
391
+ />
392
+ </div>
393
+ `,
394
+ }),
395
+ args: {
396
+ items: [
397
+ ...mockItems,
398
+ {
399
+ companyName: 'Company 6',
400
+ orgNumber: '6666666666',
401
+ companyFqn: 'Company 6 | 6666666666',
402
+ },
403
+ {
404
+ companyName: 'Company 7',
405
+ orgNumber: '7777777777',
406
+ companyFqn: 'Company 7 | 7777777777',
407
+ },
408
+ {
409
+ companyName: 'Company 8',
410
+ orgNumber: '8888888888',
411
+ companyFqn: 'Company 8 | 8888888888',
412
+ },
413
+ {
414
+ companyName: 'Company 9',
415
+ orgNumber: '9999999999',
416
+ companyFqn: 'Company 9 | 9999999999',
417
+ },
418
+ {
419
+ companyName: 'Company 10',
420
+ orgNumber: '1010101010',
421
+ companyFqn: 'Company 10 | 1010101010',
422
+ },
423
+ ],
424
+ page: 1,
425
+ totalPages: 2,
426
+ label: 'Sök organisation',
427
+ },
428
+ }