@citizenplane/pimp 18.18.0 → 18.19.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.
- package/dist/components/BaseInputLabel.vue.d.ts +1 -1
- package/dist/components/BaseInputLabel.vue.d.ts.map +1 -1
- package/dist/components/CpAncillaryCategory.vue.d.ts +42 -0
- package/dist/components/CpAncillaryCategory.vue.d.ts.map +1 -0
- package/dist/components/CpCheckbox.vue.d.ts +2 -2
- package/dist/components/CpCheckbox.vue.d.ts.map +1 -1
- package/dist/components/CpDate.vue.d.ts +1 -1
- package/dist/components/CpDate.vue.d.ts.map +1 -1
- package/dist/components/CpMenu.vue.d.ts +1 -1
- package/dist/components/CpMenu.vue.d.ts.map +1 -1
- package/dist/components/CpSelect.vue.d.ts +1 -1
- package/dist/components/CpSelect.vue.d.ts.map +1 -1
- package/dist/components/CpSwitch.vue.d.ts +4 -4
- package/dist/components/CpSwitch.vue.d.ts.map +1 -1
- package/dist/components/CpTelInput.vue.d.ts +2 -2
- package/dist/components/CpTelInput.vue.d.ts.map +1 -1
- package/dist/components/CpTextarea.vue.d.ts +1 -1
- package/dist/components/CpTextarea.vue.d.ts.map +1 -1
- package/dist/components/CpTooltip.vue.d.ts +1 -1
- package/dist/components/CpTooltip.vue.d.ts.map +1 -1
- package/dist/components/CpTransitionCounter.vue.d.ts +1 -1
- package/dist/components/CpTransitionCounter.vue.d.ts.map +1 -1
- package/dist/components/index.d.ts +3 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/constants/CpAncillaryCategoryTypes.d.ts +12 -0
- package/dist/constants/CpAncillaryCategoryTypes.d.ts.map +1 -0
- package/dist/pimp.es.js +1862 -1757
- package/dist/pimp.umd.js +50 -50
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/assets/images/ancillary-categories/baggages.png +0 -0
- package/src/assets/images/ancillary-categories/carbon-offset.png +0 -0
- package/src/assets/images/ancillary-categories/insurance.png +0 -0
- package/src/assets/images/ancillary-categories/pets.png +0 -0
- package/src/assets/images/ancillary-categories/restricted.png +0 -0
- package/src/assets/images/ancillary-categories/sport-equipments.png +0 -0
- package/src/components/CpAncillaryCategory.vue +310 -0
- package/src/components/index.ts +4 -0
- package/src/constants/CpAncillaryCategoryTypes.ts +14 -0
- package/src/stories/CpAncillaryCategory.stories.ts +263 -0
- package/src/stories/CpAncillaryItem.stories.ts +1 -1
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="cpAncillaryCategory">
|
|
3
|
+
<div class="cpAncillaryCategory__header">
|
|
4
|
+
<button class="cpAncillaryCategory__lead" type="button" @click="handleToggle">
|
|
5
|
+
<div class="cpAncillaryCategory__visual">
|
|
6
|
+
<img
|
|
7
|
+
v-if="visual"
|
|
8
|
+
alt=""
|
|
9
|
+
class="cpAncillaryCategory__visualImage"
|
|
10
|
+
:class="visualImageDynamicClass"
|
|
11
|
+
:src="visual"
|
|
12
|
+
/>
|
|
13
|
+
<cp-icon v-else class="cpAncillaryCategory__fallbackIcon" size="40" type="shopping-bag" />
|
|
14
|
+
</div>
|
|
15
|
+
<div class="cpAncillaryCategory__texts">
|
|
16
|
+
<span :id="titleId" class="cpAncillaryCategory__title">{{ title }}</span>
|
|
17
|
+
<span class="cpAncillaryCategory__description">{{ description }}</span>
|
|
18
|
+
</div>
|
|
19
|
+
</button>
|
|
20
|
+
<div v-if="isSwitch" class="cpAncillaryCategory__trailing">
|
|
21
|
+
<span v-if="hasFromPrice" class="cpAncillaryCategory__price">{{ formattedPrice }}</span>
|
|
22
|
+
<cp-switch v-model="checked" color="accent" />
|
|
23
|
+
</div>
|
|
24
|
+
<div v-else-if="isActuallyExpandable" class="cpAncillaryCategory__trailing">
|
|
25
|
+
<span v-if="hasFromPrice" class="cpAncillaryCategory__fromPriceClip">
|
|
26
|
+
<span :aria-hidden="isOpen" class="cpAncillaryCategory__fromPrice" :class="fromPriceDynamicClass">
|
|
27
|
+
<span class="cpAncillaryCategory__fromPriceLabel">{{ fromPriceLabel }}</span>
|
|
28
|
+
<span class="cpAncillaryCategory__fromPriceAmount">{{ formattedPrice }}</span>
|
|
29
|
+
</span>
|
|
30
|
+
</span>
|
|
31
|
+
<button
|
|
32
|
+
:aria-controls="bodyId"
|
|
33
|
+
:aria-expanded="isOpen"
|
|
34
|
+
:aria-labelledby="titleId"
|
|
35
|
+
class="cpAncillaryCategory__toggle"
|
|
36
|
+
type="button"
|
|
37
|
+
@click="handleToggle"
|
|
38
|
+
>
|
|
39
|
+
<cp-icon class="cpAncillaryCategory__chevron" :class="chevronDynamicClass" size="24" type="chevron-down" />
|
|
40
|
+
</button>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
<cp-transition-expand v-if="isActuallyExpandable">
|
|
44
|
+
<div v-if="isOpen">
|
|
45
|
+
<div :id="bodyId" class="cpAncillaryCategory__body">
|
|
46
|
+
<slot />
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</cp-transition-expand>
|
|
50
|
+
<div v-else-if="!isSwitch" class="cpAncillaryCategory__body">
|
|
51
|
+
<slot />
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</template>
|
|
55
|
+
|
|
56
|
+
<script setup lang="ts">
|
|
57
|
+
import { computed, ref, useId } from 'vue'
|
|
58
|
+
|
|
59
|
+
import CpIcon from '@/components/CpIcon.vue'
|
|
60
|
+
import CpSwitch from '@/components/CpSwitch.vue'
|
|
61
|
+
import CpTransitionExpand from '@/components/CpTransitionExpand.vue'
|
|
62
|
+
|
|
63
|
+
import BaggagesVisual from '@/assets/images/ancillary-categories/baggages.png'
|
|
64
|
+
import CarbonOffsetVisual from '@/assets/images/ancillary-categories/carbon-offset.png'
|
|
65
|
+
import InsuranceVisual from '@/assets/images/ancillary-categories/insurance.png'
|
|
66
|
+
import PetsVisual from '@/assets/images/ancillary-categories/pets.png'
|
|
67
|
+
import RestrictedVisual from '@/assets/images/ancillary-categories/restricted.png'
|
|
68
|
+
import SportEquipmentsVisual from '@/assets/images/ancillary-categories/sport-equipments.png'
|
|
69
|
+
import { CpAncillaryCategoryTypes, isSwitchCategory } from '@/constants/CpAncillaryCategoryTypes'
|
|
70
|
+
|
|
71
|
+
interface Props {
|
|
72
|
+
currency?: string
|
|
73
|
+
description: string
|
|
74
|
+
fromPrice?: number
|
|
75
|
+
fromPriceLabel?: string
|
|
76
|
+
isDefaultClosed?: boolean
|
|
77
|
+
isExpandable?: boolean
|
|
78
|
+
locale?: string
|
|
79
|
+
title: string
|
|
80
|
+
type: CpAncillaryCategoryTypes
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
84
|
+
fromPriceLabel: 'From',
|
|
85
|
+
isDefaultClosed: false,
|
|
86
|
+
isExpandable: false,
|
|
87
|
+
locale: () => (typeof navigator === 'undefined' ? 'en' : navigator.language),
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
const checked = defineModel<boolean>('checked', { default: false })
|
|
91
|
+
|
|
92
|
+
const isOpen = ref(!props.isDefaultClosed)
|
|
93
|
+
|
|
94
|
+
const isSwitch = computed(() => isSwitchCategory(props.type))
|
|
95
|
+
const isActuallyExpandable = computed(() => props.isExpandable && !isSwitch.value)
|
|
96
|
+
|
|
97
|
+
const toggleId = useId()
|
|
98
|
+
const titleId = computed(() => `${toggleId}-title`)
|
|
99
|
+
const bodyId = computed(() => `${toggleId}-body`)
|
|
100
|
+
|
|
101
|
+
const categoryVisuals: Partial<Record<CpAncillaryCategoryTypes, string>> = {
|
|
102
|
+
[CpAncillaryCategoryTypes.BAGGAGES]: BaggagesVisual,
|
|
103
|
+
[CpAncillaryCategoryTypes.CARBON_OFFSET]: CarbonOffsetVisual,
|
|
104
|
+
[CpAncillaryCategoryTypes.INSURANCE]: InsuranceVisual,
|
|
105
|
+
[CpAncillaryCategoryTypes.PETS]: PetsVisual,
|
|
106
|
+
[CpAncillaryCategoryTypes.RESTRICTED]: RestrictedVisual,
|
|
107
|
+
[CpAncillaryCategoryTypes.SPORT_EQUIPMENTS]: SportEquipmentsVisual,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const visual = computed(() => categoryVisuals[props.type] ?? null)
|
|
111
|
+
|
|
112
|
+
const visualImageDynamicClass = computed(() => ({
|
|
113
|
+
'cpAncillaryCategory__visualImage--isBaggages': props.type === CpAncillaryCategoryTypes.BAGGAGES,
|
|
114
|
+
}))
|
|
115
|
+
|
|
116
|
+
const formattedPrice = computed(() => {
|
|
117
|
+
if (props.fromPrice === undefined || !props.currency) return null
|
|
118
|
+
return new Intl.NumberFormat(props.locale, { style: 'currency', currency: props.currency }).format(
|
|
119
|
+
props.fromPrice / 100,
|
|
120
|
+
)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
const hasFromPrice = computed(() => formattedPrice.value !== null)
|
|
124
|
+
|
|
125
|
+
const fromPriceDynamicClass = computed(() => ({ 'cpAncillaryCategory__fromPrice--isHidden': isOpen.value }))
|
|
126
|
+
|
|
127
|
+
const chevronDynamicClass = computed(() => ({ 'cpAncillaryCategory__chevron--isRotated': isOpen.value }))
|
|
128
|
+
|
|
129
|
+
const handleToggle = () => {
|
|
130
|
+
isOpen.value = !isOpen.value
|
|
131
|
+
}
|
|
132
|
+
</script>
|
|
133
|
+
|
|
134
|
+
<style lang="scss">
|
|
135
|
+
.cpAncillaryCategory {
|
|
136
|
+
display: flex;
|
|
137
|
+
box-sizing: border-box;
|
|
138
|
+
flex-direction: column;
|
|
139
|
+
padding: var(--cp-spacing-sm);
|
|
140
|
+
border: fn.px-to-rem(1) solid var(--cp-border-soft);
|
|
141
|
+
border-radius: var(--cp-radius-xl);
|
|
142
|
+
background: var(--cp-background-primary);
|
|
143
|
+
box-shadow: var(--cp-shadows-xs);
|
|
144
|
+
gap: var(--cp-spacing-sm);
|
|
145
|
+
|
|
146
|
+
&__header {
|
|
147
|
+
display: flex;
|
|
148
|
+
width: 100%;
|
|
149
|
+
align-items: center;
|
|
150
|
+
padding: var(--cp-spacing-lg);
|
|
151
|
+
border-radius: var(--cp-radius-lg);
|
|
152
|
+
gap: var(--cp-spacing-lg);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
&__trailing {
|
|
156
|
+
display: flex;
|
|
157
|
+
flex: none;
|
|
158
|
+
align-items: center;
|
|
159
|
+
justify-content: flex-end;
|
|
160
|
+
padding: 0 var(--cp-spacing-xl);
|
|
161
|
+
border-radius: var(--cp-radius-lg);
|
|
162
|
+
gap: var(--cp-spacing-lg);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
&__price {
|
|
166
|
+
color: var(--cp-text-accent-primary);
|
|
167
|
+
font-size: var(--cp-text-size-lg);
|
|
168
|
+
font-variant-numeric: lining-nums tabular-nums;
|
|
169
|
+
font-weight: 600;
|
|
170
|
+
line-height: var(--cp-text-lg-line-height);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
&__fromPriceClip {
|
|
174
|
+
overflow: hidden;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
&__fromPrice {
|
|
178
|
+
display: flex;
|
|
179
|
+
align-items: center;
|
|
180
|
+
gap: var(--cp-spacing-md);
|
|
181
|
+
opacity: 1;
|
|
182
|
+
transform: translate3d(0, 0, 0);
|
|
183
|
+
transition:
|
|
184
|
+
opacity 200ms ease,
|
|
185
|
+
transform 200ms ease;
|
|
186
|
+
|
|
187
|
+
&--isHidden {
|
|
188
|
+
opacity: 0;
|
|
189
|
+
pointer-events: none;
|
|
190
|
+
transform: translate3d(0, 20px, 0);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
&__fromPriceLabel {
|
|
195
|
+
color: var(--cp-text-secondary);
|
|
196
|
+
font-size: var(--cp-text-size-lg);
|
|
197
|
+
font-variant-numeric: lining-nums tabular-nums;
|
|
198
|
+
line-height: var(--cp-text-lg-line-height);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
&__fromPriceAmount {
|
|
202
|
+
color: var(--cp-text-primary);
|
|
203
|
+
font-size: var(--cp-text-size-lg);
|
|
204
|
+
font-variant-numeric: lining-nums tabular-nums;
|
|
205
|
+
font-weight: 600;
|
|
206
|
+
line-height: var(--cp-text-lg-line-height);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
&__toggle {
|
|
210
|
+
display: flex;
|
|
211
|
+
flex: none;
|
|
212
|
+
align-items: center;
|
|
213
|
+
justify-content: center;
|
|
214
|
+
padding: var(--cp-spacing-xs);
|
|
215
|
+
border: 0;
|
|
216
|
+
border-radius: 50%;
|
|
217
|
+
appearance: none;
|
|
218
|
+
background: transparent;
|
|
219
|
+
color: inherit;
|
|
220
|
+
cursor: pointer;
|
|
221
|
+
transition: background-color 200ms ease;
|
|
222
|
+
|
|
223
|
+
@media (hover: hover) {
|
|
224
|
+
&:hover {
|
|
225
|
+
background: var(--cp-background-secondary-hover);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
&:focus-visible {
|
|
230
|
+
border-radius: 50%;
|
|
231
|
+
background: var(--cp-background-secondary-hover);
|
|
232
|
+
|
|
233
|
+
@extend %u-focus-outline;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
&__lead {
|
|
238
|
+
@extend %u-focus-outline;
|
|
239
|
+
|
|
240
|
+
display: flex;
|
|
241
|
+
width: 100%;
|
|
242
|
+
min-width: 0;
|
|
243
|
+
flex: auto;
|
|
244
|
+
align-items: center;
|
|
245
|
+
gap: var(--cp-spacing-xl);
|
|
246
|
+
text-align: left;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
&__visual {
|
|
250
|
+
display: flex;
|
|
251
|
+
width: fn.px-to-rem(96);
|
|
252
|
+
height: fn.px-to-rem(96);
|
|
253
|
+
flex: none;
|
|
254
|
+
align-items: center;
|
|
255
|
+
justify-content: center;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
&__visualImage {
|
|
259
|
+
width: fn.px-to-rem(72);
|
|
260
|
+
height: fn.px-to-rem(72);
|
|
261
|
+
object-fit: contain;
|
|
262
|
+
|
|
263
|
+
&--isBaggages {
|
|
264
|
+
width: fn.px-to-rem(84);
|
|
265
|
+
height: fn.px-to-rem(84);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
&__fallbackIcon {
|
|
270
|
+
color: var(--cp-text-tertiary);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
&__texts {
|
|
274
|
+
display: flex;
|
|
275
|
+
min-width: 0;
|
|
276
|
+
flex-direction: column;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
&__title {
|
|
280
|
+
color: var(--cp-text-accent-primary);
|
|
281
|
+
font-size: var(--cp-text-size-xl);
|
|
282
|
+
font-weight: 600;
|
|
283
|
+
line-height: var(--cp-text-xl-line-height);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
&__description {
|
|
287
|
+
color: var(--cp-text-primary);
|
|
288
|
+
font-size: var(--cp-text-size-md);
|
|
289
|
+
line-height: var(--cp-text-md-line-height);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
&__chevron {
|
|
293
|
+
flex: none;
|
|
294
|
+
color: var(--cp-text-accent-primary);
|
|
295
|
+
transition: rotate 300ms ease;
|
|
296
|
+
|
|
297
|
+
&--isRotated {
|
|
298
|
+
rotate: 180deg;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
&__body {
|
|
303
|
+
display: flex;
|
|
304
|
+
flex-direction: column;
|
|
305
|
+
padding: var(--cp-spacing-lg);
|
|
306
|
+
border-radius: var(--cp-radius-lg);
|
|
307
|
+
background: var(--cp-background-primary);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
</style>
|
package/src/components/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ import CpAccordion from './CpAccordion.vue'
|
|
|
19
19
|
import CpAccordionGroup from './CpAccordionGroup.vue'
|
|
20
20
|
import CpAirlineLogo from './CpAirlineLogo.vue'
|
|
21
21
|
import CpAlert from './CpAlert.vue'
|
|
22
|
+
import CpAncillaryCategory from './CpAncillaryCategory.vue'
|
|
22
23
|
import CpAncillaryItem from './CpAncillaryItem.vue'
|
|
23
24
|
import CpBadge from './CpBadge.vue'
|
|
24
25
|
import CpBottomSheet from './CpBottomSheet.vue'
|
|
@@ -115,6 +116,7 @@ const Components = {
|
|
|
115
116
|
CpRadioNew,
|
|
116
117
|
CpCalendar,
|
|
117
118
|
CpAlert,
|
|
119
|
+
CpAncillaryCategory,
|
|
118
120
|
CpAncillaryItem,
|
|
119
121
|
CpLoader,
|
|
120
122
|
CpInput,
|
|
@@ -209,6 +211,7 @@ export type {
|
|
|
209
211
|
FlightTerminal,
|
|
210
212
|
} from '@/constants/tripDetails'
|
|
211
213
|
|
|
214
|
+
export { CpAncillaryCategoryTypes, isSwitchCategory } from '@/constants/CpAncillaryCategoryTypes'
|
|
212
215
|
export { CpAncillaryItemTypes } from '@/constants/CpAncillaryItemTypes'
|
|
213
216
|
export {
|
|
214
217
|
CpSeatMapTransitionDirections,
|
|
@@ -227,6 +230,7 @@ export {
|
|
|
227
230
|
CpAccordionGroup,
|
|
228
231
|
CpAirlineLogo,
|
|
229
232
|
CpAlert,
|
|
233
|
+
CpAncillaryCategory,
|
|
230
234
|
CpAncillaryItem,
|
|
231
235
|
CpBadge,
|
|
232
236
|
CpBottomSheet,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export enum CpAncillaryCategoryTypes {
|
|
2
|
+
AIRPORT_SERVICES = 'airportServices',
|
|
3
|
+
BAGGAGES = 'baggages',
|
|
4
|
+
CARBON_OFFSET = 'carbonOffset',
|
|
5
|
+
INSURANCE = 'insurance',
|
|
6
|
+
ON_BOARD = 'onBoard',
|
|
7
|
+
PETS = 'pets',
|
|
8
|
+
RESTRICTED = 'restricted',
|
|
9
|
+
SPORT_EQUIPMENTS = 'sportEquipments',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const SWITCH_CATEGORY_TYPES = new Set<CpAncillaryCategoryTypes>([CpAncillaryCategoryTypes.CARBON_OFFSET])
|
|
13
|
+
|
|
14
|
+
export const isSwitchCategory = (type: CpAncillaryCategoryTypes): boolean => SWITCH_CATEGORY_TYPES.has(type)
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
import CpAncillaryCategory from '@/components/CpAncillaryCategory.vue'
|
|
5
|
+
import CpAncillaryItem from '@/components/CpAncillaryItem.vue'
|
|
6
|
+
|
|
7
|
+
import { CpAncillaryCategoryTypes } from '@/constants/CpAncillaryCategoryTypes'
|
|
8
|
+
import { docLabelStyle } from '@/stories/documentationStyles'
|
|
9
|
+
|
|
10
|
+
const cardGridStyle = 'display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px;'
|
|
11
|
+
const galleryListStyle = 'display: flex; flex-direction: column; gap: 24px; width: 100%;'
|
|
12
|
+
const galleryRowStyle = 'display: flex; flex-direction: column; gap: 10px; width: 100%;'
|
|
13
|
+
|
|
14
|
+
const meta = {
|
|
15
|
+
title: 'Business/CpAncillaryCategory',
|
|
16
|
+
component: CpAncillaryCategory,
|
|
17
|
+
argTypes: {
|
|
18
|
+
currency: {
|
|
19
|
+
control: 'text',
|
|
20
|
+
description: 'ISO currency code used to format the from price',
|
|
21
|
+
},
|
|
22
|
+
description: {
|
|
23
|
+
control: 'text',
|
|
24
|
+
description: 'Description text for the category',
|
|
25
|
+
},
|
|
26
|
+
fromPrice: {
|
|
27
|
+
control: 'number',
|
|
28
|
+
description: 'From price in minor units (cents), shown when isExpandable is set',
|
|
29
|
+
},
|
|
30
|
+
fromPriceLabel: {
|
|
31
|
+
control: 'text',
|
|
32
|
+
description: 'Caption shown before the from price amount',
|
|
33
|
+
},
|
|
34
|
+
isDefaultClosed: {
|
|
35
|
+
control: 'boolean',
|
|
36
|
+
description: 'Whether an expandable category starts closed',
|
|
37
|
+
},
|
|
38
|
+
isExpandable: {
|
|
39
|
+
control: 'boolean',
|
|
40
|
+
description: 'Whether the header gets a from-price + chevron toggle collapsing the body',
|
|
41
|
+
},
|
|
42
|
+
locale: {
|
|
43
|
+
control: 'text',
|
|
44
|
+
description: 'Locale used to format the from price, defaults to the browser locale',
|
|
45
|
+
},
|
|
46
|
+
title: {
|
|
47
|
+
control: 'text',
|
|
48
|
+
description: 'Title of the category',
|
|
49
|
+
},
|
|
50
|
+
type: {
|
|
51
|
+
control: 'select',
|
|
52
|
+
options: Object.values(CpAncillaryCategoryTypes),
|
|
53
|
+
description: 'Picks the visual and, for switch categories, the trailing control',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
} satisfies Meta<typeof CpAncillaryCategory>
|
|
57
|
+
|
|
58
|
+
export default meta
|
|
59
|
+
type Story = StoryObj<typeof meta>
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Expandable category with a from-price. Use the controls to experiment with
|
|
63
|
+
* each prop in isolation.
|
|
64
|
+
*/
|
|
65
|
+
export const Default: Story = {
|
|
66
|
+
args: {
|
|
67
|
+
title: 'Baggages',
|
|
68
|
+
description: 'Check in your bags for the flight',
|
|
69
|
+
type: CpAncillaryCategoryTypes.BAGGAGES,
|
|
70
|
+
isExpandable: true,
|
|
71
|
+
fromPrice: 2990,
|
|
72
|
+
fromPriceLabel: 'From',
|
|
73
|
+
currency: 'EUR',
|
|
74
|
+
},
|
|
75
|
+
render: (args) => ({
|
|
76
|
+
components: { CpAncillaryCategory, CpAncillaryItem },
|
|
77
|
+
setup() {
|
|
78
|
+
const quantities = ref({ checkedBag: 0, cabinBag: 0, handBag: 0 })
|
|
79
|
+
return { args, cardGridStyle, quantities }
|
|
80
|
+
},
|
|
81
|
+
template: `
|
|
82
|
+
<div style="padding: 20px; min-width: 15rem;">
|
|
83
|
+
<CpAncillaryCategory v-bind="args">
|
|
84
|
+
<div :style="cardGridStyle">
|
|
85
|
+
<CpAncillaryItem
|
|
86
|
+
v-model:quantity="quantities.checkedBag"
|
|
87
|
+
title="Checked bag"
|
|
88
|
+
description="23kg included"
|
|
89
|
+
type="checkedBag"
|
|
90
|
+
:max-quantity="3"
|
|
91
|
+
max-quantity-label="max. 3"
|
|
92
|
+
:price="2990"
|
|
93
|
+
currency="EUR"
|
|
94
|
+
/>
|
|
95
|
+
<CpAncillaryItem
|
|
96
|
+
v-model:quantity="quantities.cabinBag"
|
|
97
|
+
title="Cabin bag"
|
|
98
|
+
description="10kg included"
|
|
99
|
+
type="cabinBag"
|
|
100
|
+
:max-quantity="1"
|
|
101
|
+
max-quantity-label="max. 1"
|
|
102
|
+
:price="1990"
|
|
103
|
+
currency="EUR"
|
|
104
|
+
/>
|
|
105
|
+
<CpAncillaryItem
|
|
106
|
+
v-model:quantity="quantities.handBag"
|
|
107
|
+
title="Hand bag"
|
|
108
|
+
description="Personal item"
|
|
109
|
+
type="handBag"
|
|
110
|
+
:max-quantity="1"
|
|
111
|
+
max-quantity-label="max. 1"
|
|
112
|
+
:price="990"
|
|
113
|
+
currency="EUR"
|
|
114
|
+
/>
|
|
115
|
+
</div>
|
|
116
|
+
</CpAncillaryCategory>
|
|
117
|
+
</div>
|
|
118
|
+
`,
|
|
119
|
+
}),
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* -------------------------------------------------------------------------- */
|
|
123
|
+
/* States */
|
|
124
|
+
/* -------------------------------------------------------------------------- */
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* isDefaultClosed starts the expandable category collapsed.
|
|
128
|
+
*/
|
|
129
|
+
export const Collapsed: Story = {
|
|
130
|
+
args: {
|
|
131
|
+
...Default.args,
|
|
132
|
+
isDefaultClosed: true,
|
|
133
|
+
},
|
|
134
|
+
render: (args) => ({
|
|
135
|
+
components: { CpAncillaryCategory, CpAncillaryItem },
|
|
136
|
+
setup() {
|
|
137
|
+
const quantity = ref(0)
|
|
138
|
+
return { args, cardGridStyle, quantity }
|
|
139
|
+
},
|
|
140
|
+
template: `
|
|
141
|
+
<div style="padding: 20px; min-width: 15rem;">
|
|
142
|
+
<CpAncillaryCategory v-bind="args">
|
|
143
|
+
<div :style="cardGridStyle">
|
|
144
|
+
<CpAncillaryItem
|
|
145
|
+
v-model:quantity="quantity"
|
|
146
|
+
title="Checked bag"
|
|
147
|
+
description="23kg included"
|
|
148
|
+
type="checkedBag"
|
|
149
|
+
:max-quantity="3"
|
|
150
|
+
max-quantity-label="max. 3"
|
|
151
|
+
:price="2990"
|
|
152
|
+
currency="EUR"
|
|
153
|
+
/>
|
|
154
|
+
</div>
|
|
155
|
+
</CpAncillaryCategory>
|
|
156
|
+
</div>
|
|
157
|
+
`,
|
|
158
|
+
}),
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* isExpandable false: no from-price, no chevron, the body is always rendered.
|
|
163
|
+
*/
|
|
164
|
+
export const NotExpandable: Story = {
|
|
165
|
+
args: {
|
|
166
|
+
title: 'Insurance',
|
|
167
|
+
description: 'Protect your trip against the unexpected',
|
|
168
|
+
type: CpAncillaryCategoryTypes.INSURANCE,
|
|
169
|
+
isExpandable: false,
|
|
170
|
+
},
|
|
171
|
+
render: (args) => ({
|
|
172
|
+
components: { CpAncillaryCategory, CpAncillaryItem },
|
|
173
|
+
setup() {
|
|
174
|
+
const quantity = ref(0)
|
|
175
|
+
return { args, cardGridStyle, quantity }
|
|
176
|
+
},
|
|
177
|
+
template: `
|
|
178
|
+
<div style="padding: 20px; min-width: 15rem;">
|
|
179
|
+
<CpAncillaryCategory v-bind="args">
|
|
180
|
+
<div :style="cardGridStyle">
|
|
181
|
+
<CpAncillaryItem
|
|
182
|
+
v-model:quantity="quantity"
|
|
183
|
+
title="Cancellation insurance"
|
|
184
|
+
description="Refund protection for your trip"
|
|
185
|
+
type="cancellationInsurance"
|
|
186
|
+
:max-quantity="1"
|
|
187
|
+
max-quantity-label="max. 1"
|
|
188
|
+
:price="1490"
|
|
189
|
+
currency="EUR"
|
|
190
|
+
/>
|
|
191
|
+
</div>
|
|
192
|
+
</CpAncillaryCategory>
|
|
193
|
+
</div>
|
|
194
|
+
`,
|
|
195
|
+
}),
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Switch categories (carbonOffset today) show a formatted price and a switch
|
|
200
|
+
* instead of a chevron, and are never expandable.
|
|
201
|
+
*/
|
|
202
|
+
export const Switch: Story = {
|
|
203
|
+
args: {
|
|
204
|
+
title: 'Carbon offset',
|
|
205
|
+
description: 'Compensate the CO2 emissions of your flight',
|
|
206
|
+
type: CpAncillaryCategoryTypes.CARBON_OFFSET,
|
|
207
|
+
fromPrice: 490,
|
|
208
|
+
currency: 'EUR',
|
|
209
|
+
},
|
|
210
|
+
render: (args) => ({
|
|
211
|
+
components: { CpAncillaryCategory },
|
|
212
|
+
setup() {
|
|
213
|
+
const checked = ref(false)
|
|
214
|
+
return { args, checked }
|
|
215
|
+
},
|
|
216
|
+
template: `
|
|
217
|
+
<div style="padding: 20px; min-width: 15rem;">
|
|
218
|
+
<CpAncillaryCategory v-bind="args" v-model:checked="checked" />
|
|
219
|
+
</div>
|
|
220
|
+
`,
|
|
221
|
+
}),
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* -------------------------------------------------------------------------- */
|
|
225
|
+
/* Gallery */
|
|
226
|
+
/* -------------------------------------------------------------------------- */
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Every enum value's header: each real visual, and the fallback icon for the
|
|
230
|
+
* two art-less types.
|
|
231
|
+
*/
|
|
232
|
+
export const Gallery: Story = {
|
|
233
|
+
args: {
|
|
234
|
+
...Default.args,
|
|
235
|
+
},
|
|
236
|
+
parameters: { controls: { disable: true } },
|
|
237
|
+
render: () => ({
|
|
238
|
+
components: { CpAncillaryCategory },
|
|
239
|
+
setup() {
|
|
240
|
+
const types = Object.values(CpAncillaryCategoryTypes)
|
|
241
|
+
const checkedStates = ref(Object.fromEntries(types.map((type) => [type, false])))
|
|
242
|
+
return { types, checkedStates, galleryListStyle, galleryRowStyle, docLabelStyle }
|
|
243
|
+
},
|
|
244
|
+
template: `
|
|
245
|
+
<div :style="galleryListStyle">
|
|
246
|
+
<div v-for="type in types" :key="type" :style="galleryRowStyle">
|
|
247
|
+
<span :style="docLabelStyle">{{ type }}</span>
|
|
248
|
+
<CpAncillaryCategory
|
|
249
|
+
v-model:checked="checkedStates[type]"
|
|
250
|
+
:title="type"
|
|
251
|
+
description="Sample description"
|
|
252
|
+
:type="type"
|
|
253
|
+
is-expandable
|
|
254
|
+
:from-price="1000"
|
|
255
|
+
currency="EUR"
|
|
256
|
+
>
|
|
257
|
+
<span :style="docLabelStyle">Body content</span>
|
|
258
|
+
</CpAncillaryCategory>
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
`,
|
|
262
|
+
}),
|
|
263
|
+
}
|
|
@@ -9,7 +9,7 @@ import { docCellStyle, docLabelStyle, docRowWrapStyle } from '@/stories/document
|
|
|
9
9
|
const galleryCellStyle = `${docCellStyle} width: 260px;`
|
|
10
10
|
|
|
11
11
|
const meta = {
|
|
12
|
-
title: '
|
|
12
|
+
title: 'Business/CpAncillaryItem',
|
|
13
13
|
component: CpAncillaryItem,
|
|
14
14
|
argTypes: {
|
|
15
15
|
currency: {
|