@citizenplane/pimp 18.21.2 → 18.23.0
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/CpAncillaryFlightSelection.vue.d.ts.map +1 -1
- package/dist/components/CpBasket.vue.d.ts +25 -0
- package/dist/components/CpBasket.vue.d.ts.map +1 -0
- package/dist/components/CpFlightTabs.vue.d.ts +17 -0
- package/dist/components/CpFlightTabs.vue.d.ts.map +1 -0
- package/dist/components/index.d.ts +5 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/constants/CpAncillaryFlightSelectionTypes.d.ts +1 -7
- package/dist/constants/CpAncillaryFlightSelectionTypes.d.ts.map +1 -1
- package/dist/constants/CpBasketTypes.d.ts +16 -0
- package/dist/constants/CpBasketTypes.d.ts.map +1 -0
- package/dist/constants/CpFlightTabsTypes.d.ts +9 -0
- package/dist/constants/CpFlightTabsTypes.d.ts.map +1 -0
- package/dist/pimp.es.js +1722 -1625
- package/dist/pimp.umd.js +40 -40
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/CpAncillaryFlightSelection.vue +10 -126
- package/src/components/CpBasket.vue +271 -0
- package/src/components/CpFlightTabs.vue +128 -0
- package/src/components/index.ts +8 -0
- package/src/constants/CpAncillaryFlightSelectionTypes.ts +1 -7
- package/src/constants/CpBasketTypes.ts +17 -0
- package/src/constants/CpFlightTabsTypes.ts +8 -0
- package/src/stories/CpBasket.stories.ts +323 -0
- package/src/stories/CpFlightTabs.stories.ts +142 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import type { Args, Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
import type { CpBasketRow } from '@/constants/CpBasketTypes'
|
|
5
|
+
|
|
6
|
+
import CpBasket from '@/components/CpBasket.vue'
|
|
7
|
+
import CpButton from '@/components/CpButton.vue'
|
|
8
|
+
import CpIcon from '@/components/CpIcon.vue'
|
|
9
|
+
import CpTrip from '@/components/CpTrip.vue'
|
|
10
|
+
|
|
11
|
+
const outboundTrip = {
|
|
12
|
+
arrivalDate: {
|
|
13
|
+
formatted: 'Tue, 10 Jun 2026',
|
|
14
|
+
raw: '2026-06-10',
|
|
15
|
+
},
|
|
16
|
+
arrivalTime: '10:20',
|
|
17
|
+
labels: {
|
|
18
|
+
nonStop: 'Direct',
|
|
19
|
+
},
|
|
20
|
+
dayCountAfterDeparture: 0,
|
|
21
|
+
departureDate: {
|
|
22
|
+
formatted: 'Tue, 10 Jun 2026',
|
|
23
|
+
raw: '2026-06-10',
|
|
24
|
+
},
|
|
25
|
+
departureTime: '09:00',
|
|
26
|
+
destination: {
|
|
27
|
+
iataCode: 'ORY',
|
|
28
|
+
name: 'Paris Orly Airport',
|
|
29
|
+
},
|
|
30
|
+
flightDuration: {
|
|
31
|
+
formatted: '1h 20m',
|
|
32
|
+
raw: 'PT1H20M',
|
|
33
|
+
},
|
|
34
|
+
airlines: [
|
|
35
|
+
{
|
|
36
|
+
iataCode: 'AF',
|
|
37
|
+
name: 'Air France',
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
origin: {
|
|
41
|
+
iataCode: 'CDG',
|
|
42
|
+
name: 'Paris Charles de Gaulle Airport',
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const returnTrip = {
|
|
47
|
+
arrivalDate: {
|
|
48
|
+
formatted: 'Fri, 13 Jun 2026',
|
|
49
|
+
raw: '2026-06-13',
|
|
50
|
+
},
|
|
51
|
+
arrivalTime: '19:50',
|
|
52
|
+
labels: {
|
|
53
|
+
nonStop: 'Direct',
|
|
54
|
+
},
|
|
55
|
+
dayCountAfterDeparture: 0,
|
|
56
|
+
departureDate: {
|
|
57
|
+
formatted: 'Fri, 13 Jun 2026',
|
|
58
|
+
raw: '2026-06-13',
|
|
59
|
+
},
|
|
60
|
+
departureTime: '18:30',
|
|
61
|
+
destination: {
|
|
62
|
+
iataCode: 'CDG',
|
|
63
|
+
name: 'Paris Charles de Gaulle Airport',
|
|
64
|
+
},
|
|
65
|
+
flightDuration: {
|
|
66
|
+
formatted: '1h 20m',
|
|
67
|
+
raw: 'PT1H20M',
|
|
68
|
+
},
|
|
69
|
+
airlines: [
|
|
70
|
+
{
|
|
71
|
+
iataCode: 'AF',
|
|
72
|
+
name: 'Air France',
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
origin: {
|
|
76
|
+
iataCode: 'ORY',
|
|
77
|
+
name: 'Paris Orly Airport',
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const seatsCategory = { id: 'seats', label: 'Seats', subtotal: '24,00 €' }
|
|
82
|
+
const baggageCategory = { id: 'baggage', label: 'Baggage', subtotal: '20,00 €' }
|
|
83
|
+
const extrasCategory = { id: 'extras', label: 'Extras', subtotal: '3,00 €' }
|
|
84
|
+
|
|
85
|
+
const outboundGroup = { id: 'CDG-ORY', label: 'CDG → ORY' }
|
|
86
|
+
const returnGroup = { id: 'ORY-CDG', label: 'ORY → CDG' }
|
|
87
|
+
|
|
88
|
+
const rows: CpBasketRow[] = [
|
|
89
|
+
{ label: 'Seat 12A', value: '12,00 €', category: seatsCategory, group: outboundGroup },
|
|
90
|
+
{ label: 'Seat 14C', value: '12,00 €', category: seatsCategory, group: returnGroup },
|
|
91
|
+
{ label: '2 × Cabin baggage 10kg', value: '20,00 €', category: baggageCategory, group: outboundGroup },
|
|
92
|
+
{ label: 'Carbon offset', value: '3,00 €', category: extrasCategory },
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
const fakeRowPool: CpBasketRow[] = [
|
|
96
|
+
{ label: 'Extra bag 20kg', value: '15,00 €', category: baggageCategory, group: outboundGroup },
|
|
97
|
+
{ label: 'Seat 15B', value: '10,00 €', category: seatsCategory, group: returnGroup },
|
|
98
|
+
{ label: 'Meal', value: '9,00 €', category: extrasCategory },
|
|
99
|
+
{ label: 'Priority boarding', value: '8,00 €', category: extrasCategory, group: null },
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
const flightsCategory = { id: 'flights', label: 'Flights', subtotal: '159,80 €' }
|
|
103
|
+
const hiddenSeatsCategory = { id: 'seats', label: 'Seats', subtotal: '99,60 €' }
|
|
104
|
+
const tripExtrasCategory = { id: 'tripExtras', label: 'Trip extras', subtotal: '269,40 €' }
|
|
105
|
+
const taxesCategory = { id: 'taxes', label: 'Taxes & Fees', subtotal: '14,30 €' }
|
|
106
|
+
|
|
107
|
+
const hiddenDetailsRows: CpBasketRow[] = [
|
|
108
|
+
{ label: 'CDG → MRS', value: '79,90 €', category: flightsCategory, group: outboundGroup },
|
|
109
|
+
{ label: 'MRS → CDG', value: '79,90 €', category: flightsCategory, group: returnGroup },
|
|
110
|
+
{ label: 'Extra legroom', value: '99,60 €', category: hiddenSeatsCategory },
|
|
111
|
+
{ label: 'Priority boarding', value: '269,40 €', category: tripExtrasCategory },
|
|
112
|
+
{ label: 'VAT', value: '14,30 €', category: taxesCategory },
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
const meta = {
|
|
116
|
+
title: 'Business/CpBasket',
|
|
117
|
+
component: CpBasket,
|
|
118
|
+
argTypes: {
|
|
119
|
+
hideDetailsOnCategories: {
|
|
120
|
+
control: 'object',
|
|
121
|
+
description: 'Category ids rendered as a title-only line, with no groups or rows',
|
|
122
|
+
table: { defaultValue: { summary: '[]' } },
|
|
123
|
+
},
|
|
124
|
+
rows: {
|
|
125
|
+
control: 'object',
|
|
126
|
+
description: 'Flat list of basket rows, each carrying its own category and group',
|
|
127
|
+
},
|
|
128
|
+
total: {
|
|
129
|
+
control: 'text',
|
|
130
|
+
description: 'Preformatted total amount',
|
|
131
|
+
},
|
|
132
|
+
totalLabel: {
|
|
133
|
+
control: 'text',
|
|
134
|
+
description: 'Label shown next to the total, translated by the consumer',
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
} satisfies Meta<typeof CpBasket>
|
|
138
|
+
|
|
139
|
+
export default meta
|
|
140
|
+
|
|
141
|
+
type Story = StoryObj<typeof meta>
|
|
142
|
+
|
|
143
|
+
const defaultRender = (args: Args) => ({
|
|
144
|
+
components: { CpBasket, CpTrip },
|
|
145
|
+
setup() {
|
|
146
|
+
return { args, outboundTrip, returnTrip }
|
|
147
|
+
},
|
|
148
|
+
template: `
|
|
149
|
+
<CpBasket v-bind="args">
|
|
150
|
+
<template #leading>
|
|
151
|
+
<div class="cpBasket__leg">
|
|
152
|
+
<p class="cpBasket__legDate">{{ outboundTrip.departureDate.formatted }}</p>
|
|
153
|
+
<CpTrip hide-airlines :trip="outboundTrip" />
|
|
154
|
+
</div>
|
|
155
|
+
<div class="cpBasket__leg">
|
|
156
|
+
<p class="cpBasket__legDate">{{ returnTrip.departureDate.formatted }}</p>
|
|
157
|
+
<CpTrip hide-airlines :trip="returnTrip" />
|
|
158
|
+
</div>
|
|
159
|
+
</template>
|
|
160
|
+
</CpBasket>
|
|
161
|
+
`,
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
const defaultStoryRender = (args: Args) => ({
|
|
165
|
+
components: { CpBasket, CpButton, CpTrip },
|
|
166
|
+
setup() {
|
|
167
|
+
const storyRows = ref<CpBasketRow[]>([...rows])
|
|
168
|
+
const fakeRowCount = ref(0)
|
|
169
|
+
|
|
170
|
+
const addFakeRow = () => {
|
|
171
|
+
const pick = fakeRowPool[Math.floor(Math.random() * fakeRowPool.length)]
|
|
172
|
+
fakeRowCount.value += 1
|
|
173
|
+
storyRows.value = [...storyRows.value, { ...pick, label: `${pick.label} #${fakeRowCount.value}` }]
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return { addFakeRow, args, outboundTrip, returnTrip, storyRows }
|
|
177
|
+
},
|
|
178
|
+
template: `
|
|
179
|
+
<div style="display: flex; flex-direction: column; gap: 16px; max-width: 415px;">
|
|
180
|
+
<CpButton @click="addFakeRow">Add fake row</CpButton>
|
|
181
|
+
<CpBasket v-bind="args" :rows="storyRows">
|
|
182
|
+
<template #leading>
|
|
183
|
+
<div class="cpBasket__leg">
|
|
184
|
+
<p class="cpBasket__legDate">{{ outboundTrip.departureDate.formatted }}</p>
|
|
185
|
+
<CpTrip hide-airlines :trip="outboundTrip" />
|
|
186
|
+
</div>
|
|
187
|
+
<div class="cpBasket__leg">
|
|
188
|
+
<p class="cpBasket__legDate">{{ returnTrip.departureDate.formatted }}</p>
|
|
189
|
+
<CpTrip hide-airlines :trip="returnTrip" />
|
|
190
|
+
</div>
|
|
191
|
+
</template>
|
|
192
|
+
</CpBasket>
|
|
193
|
+
</div>
|
|
194
|
+
`,
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Three categories (seats, baggage, extras) with rows grouped per route in the
|
|
199
|
+
* `#leading` slot rendering the two `cp-trip` legs. The button above the
|
|
200
|
+
* basket appends one random fake row per click (sometimes ungrouped) to
|
|
201
|
+
* eyeball the `cp-transition-list-items` insert animation.
|
|
202
|
+
*/
|
|
203
|
+
export const Default: Story = {
|
|
204
|
+
args: {
|
|
205
|
+
rows,
|
|
206
|
+
total: '47,00 €',
|
|
207
|
+
totalLabel: 'Total',
|
|
208
|
+
},
|
|
209
|
+
render: defaultStoryRender,
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* No rows: only the `#leading` slot and the total render, the details block
|
|
214
|
+
* collapses via `cp-transition-expand`.
|
|
215
|
+
*/
|
|
216
|
+
export const EmptyRows: Story = {
|
|
217
|
+
args: {
|
|
218
|
+
rows: [],
|
|
219
|
+
total: '0,00 €',
|
|
220
|
+
totalLabel: 'Total',
|
|
221
|
+
},
|
|
222
|
+
render: defaultRender,
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* One category mixing a labelled group with ungrouped rows whose `group` is
|
|
227
|
+
* either omitted or explicitly `null` — both render the same way: no label,
|
|
228
|
+
* no indentation, after the labelled group.
|
|
229
|
+
*/
|
|
230
|
+
export const GroupedAndUngroupedRows: Story = {
|
|
231
|
+
args: {
|
|
232
|
+
rows: [
|
|
233
|
+
{
|
|
234
|
+
label: 'Seat 12A',
|
|
235
|
+
value: '12,00 €',
|
|
236
|
+
category: { id: 'seats', label: 'Seats', subtotal: '32,00 €' },
|
|
237
|
+
group: outboundGroup,
|
|
238
|
+
},
|
|
239
|
+
{ label: 'Seat 14C', value: '12,00 €', category: { id: 'seats', label: 'Seats', subtotal: '32,00 €' } },
|
|
240
|
+
{
|
|
241
|
+
label: 'Priority boarding',
|
|
242
|
+
value: '8,00 €',
|
|
243
|
+
category: { id: 'seats', label: 'Seats', subtotal: '32,00 €' },
|
|
244
|
+
group: null,
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
total: '32,00 €',
|
|
248
|
+
totalLabel: 'Total',
|
|
249
|
+
},
|
|
250
|
+
render: defaultRender,
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* No `#leading` slot content: the leading container is not rendered at all.
|
|
255
|
+
*/
|
|
256
|
+
export const NoLeadingSlot: Story = {
|
|
257
|
+
args: {
|
|
258
|
+
rows,
|
|
259
|
+
total: '47,00 €',
|
|
260
|
+
totalLabel: 'Total',
|
|
261
|
+
},
|
|
262
|
+
render: (args: Args) => ({
|
|
263
|
+
components: { CpBasket },
|
|
264
|
+
setup() {
|
|
265
|
+
return { args }
|
|
266
|
+
},
|
|
267
|
+
template: `<CpBasket v-bind="args" />`,
|
|
268
|
+
}),
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* `hideDetailsOnCategories` collapses "Seats", "Trip extras" and
|
|
273
|
+
* "Taxes & Fees" to their title line while "Flights" keeps its grouped rows,
|
|
274
|
+
* matching the Figma payment-summary reference.
|
|
275
|
+
*/
|
|
276
|
+
export const HiddenDetails: Story = {
|
|
277
|
+
args: {
|
|
278
|
+
hideDetailsOnCategories: ['seats', 'tripExtras', 'taxes'],
|
|
279
|
+
rows: hiddenDetailsRows,
|
|
280
|
+
total: '543,10 €',
|
|
281
|
+
totalLabel: 'Total',
|
|
282
|
+
},
|
|
283
|
+
render: defaultRender,
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* `#trailing` slot rendered after the total, with a `var(--cp-spacing-sm)`
|
|
288
|
+
* gap only appearing because the slot is provided.
|
|
289
|
+
*/
|
|
290
|
+
export const TrailingSlot: Story = {
|
|
291
|
+
args: {
|
|
292
|
+
rows,
|
|
293
|
+
total: '47,00 €',
|
|
294
|
+
totalLabel: 'Total',
|
|
295
|
+
},
|
|
296
|
+
render: (args: Args) => ({
|
|
297
|
+
components: { CpBasket, CpButton, CpIcon, CpTrip },
|
|
298
|
+
setup() {
|
|
299
|
+
return { args, outboundTrip, returnTrip }
|
|
300
|
+
},
|
|
301
|
+
template: `
|
|
302
|
+
<CpBasket v-bind="args">
|
|
303
|
+
<template #leading>
|
|
304
|
+
<div class="cpBasket__leg">
|
|
305
|
+
<p class="cpBasket__legDate">{{ outboundTrip.departureDate.formatted }}</p>
|
|
306
|
+
<CpTrip hide-airlines :trip="outboundTrip" />
|
|
307
|
+
</div>
|
|
308
|
+
<div class="cpBasket__leg">
|
|
309
|
+
<p class="cpBasket__legDate">{{ returnTrip.departureDate.formatted }}</p>
|
|
310
|
+
<CpTrip hide-airlines :trip="returnTrip" />
|
|
311
|
+
</div>
|
|
312
|
+
</template>
|
|
313
|
+
<template #trailing>
|
|
314
|
+
<CpButton appearance="secondary" color="accent" size="lg" style="width: 100%;">
|
|
315
|
+
<template #leading-icon><CpIcon type="download" /></template>
|
|
316
|
+
Download your invoice (PDF)
|
|
317
|
+
</CpButton>
|
|
318
|
+
<p style="color: var(--cp-text-secondary); font-size: 14px;">Paid on Jul 25, 2026 · card •• 4242</p>
|
|
319
|
+
</template>
|
|
320
|
+
</CpBasket>
|
|
321
|
+
`,
|
|
322
|
+
}),
|
|
323
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
import type { CpFlightTabsFlight } from '@/constants/CpFlightTabsTypes'
|
|
5
|
+
|
|
6
|
+
import CpFlightTabs from '@/components/CpFlightTabs.vue'
|
|
7
|
+
|
|
8
|
+
import { docLabelStyle } from '@/stories/documentationStyles'
|
|
9
|
+
|
|
10
|
+
const sampleFlights: CpFlightTabsFlight[] = [
|
|
11
|
+
{
|
|
12
|
+
id: 'outbound',
|
|
13
|
+
label: 'Outbound',
|
|
14
|
+
originCity: 'Paris',
|
|
15
|
+
originIata: 'CDG',
|
|
16
|
+
destinationCity: 'New York',
|
|
17
|
+
destinationIata: 'JFK',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'inbound',
|
|
21
|
+
label: 'Return',
|
|
22
|
+
originCity: 'New York',
|
|
23
|
+
originIata: 'JFK',
|
|
24
|
+
destinationCity: 'Paris',
|
|
25
|
+
destinationIata: 'CDG',
|
|
26
|
+
},
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
const noLabelFlights: CpFlightTabsFlight[] = sampleFlights.map(({ label: _label, ...flight }) => flight)
|
|
30
|
+
|
|
31
|
+
const threeFlights: CpFlightTabsFlight[] = [
|
|
32
|
+
...sampleFlights,
|
|
33
|
+
{
|
|
34
|
+
id: 'connecting',
|
|
35
|
+
label: 'Connecting',
|
|
36
|
+
originCity: 'Paris',
|
|
37
|
+
originIata: 'CDG',
|
|
38
|
+
destinationCity: 'Lyon',
|
|
39
|
+
destinationIata: 'LYS',
|
|
40
|
+
},
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
const meta = {
|
|
44
|
+
title: 'Business/CpFlightTabs',
|
|
45
|
+
component: CpFlightTabs,
|
|
46
|
+
argTypes: {
|
|
47
|
+
flights: {
|
|
48
|
+
control: 'object',
|
|
49
|
+
description: 'Flights shown as tabs',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
} satisfies Meta<typeof CpFlightTabs>
|
|
53
|
+
|
|
54
|
+
export default meta
|
|
55
|
+
type Story = StoryObj<typeof meta>
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Two flights, first one selected. The model is wired with a ref to
|
|
59
|
+
* demonstrate the round trip.
|
|
60
|
+
*/
|
|
61
|
+
export const Default: Story = {
|
|
62
|
+
args: {
|
|
63
|
+
flights: sampleFlights,
|
|
64
|
+
},
|
|
65
|
+
render: (args) => ({
|
|
66
|
+
components: { CpFlightTabs },
|
|
67
|
+
setup() {
|
|
68
|
+
const activeFlightId = ref(sampleFlights[0].id)
|
|
69
|
+
return { args, activeFlightId }
|
|
70
|
+
},
|
|
71
|
+
template: `
|
|
72
|
+
<div style="padding: 20px; width: 40rem;">
|
|
73
|
+
<CpFlightTabs v-bind="args" v-model:active-flight-id="activeFlightId" />
|
|
74
|
+
</div>
|
|
75
|
+
`,
|
|
76
|
+
}),
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Flights without a `label`: only the route line is rendered.
|
|
81
|
+
*/
|
|
82
|
+
export const NoLabels: Story = {
|
|
83
|
+
args: {
|
|
84
|
+
flights: noLabelFlights,
|
|
85
|
+
},
|
|
86
|
+
render: (args) => ({
|
|
87
|
+
components: { CpFlightTabs },
|
|
88
|
+
setup() {
|
|
89
|
+
const activeFlightId = ref(noLabelFlights[0].id)
|
|
90
|
+
return { args, activeFlightId }
|
|
91
|
+
},
|
|
92
|
+
template: `
|
|
93
|
+
<div style="padding: 20px; width: 40rem;">
|
|
94
|
+
<CpFlightTabs v-bind="args" v-model:active-flight-id="activeFlightId" />
|
|
95
|
+
</div>
|
|
96
|
+
`,
|
|
97
|
+
}),
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Three flights sharing the tab strip.
|
|
102
|
+
*/
|
|
103
|
+
export const ThreeFlights: Story = {
|
|
104
|
+
args: {
|
|
105
|
+
flights: threeFlights,
|
|
106
|
+
},
|
|
107
|
+
render: (args) => ({
|
|
108
|
+
components: { CpFlightTabs },
|
|
109
|
+
setup() {
|
|
110
|
+
const activeFlightId = ref(threeFlights[0].id)
|
|
111
|
+
return { args, activeFlightId }
|
|
112
|
+
},
|
|
113
|
+
template: `
|
|
114
|
+
<div style="padding: 20px; width: 40rem;">
|
|
115
|
+
<CpFlightTabs v-bind="args" v-model:active-flight-id="activeFlightId" />
|
|
116
|
+
</div>
|
|
117
|
+
`,
|
|
118
|
+
}),
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* At a 358px container width the tabs swap city names for IATA codes.
|
|
123
|
+
*/
|
|
124
|
+
export const Mobile: Story = {
|
|
125
|
+
args: {
|
|
126
|
+
flights: sampleFlights,
|
|
127
|
+
},
|
|
128
|
+
parameters: { controls: { disable: true } },
|
|
129
|
+
render: () => ({
|
|
130
|
+
components: { CpFlightTabs },
|
|
131
|
+
setup() {
|
|
132
|
+
const activeFlightId = ref(sampleFlights[0].id)
|
|
133
|
+
return { sampleFlights, activeFlightId, docLabelStyle }
|
|
134
|
+
},
|
|
135
|
+
template: `
|
|
136
|
+
<div style="display: flex; flex-direction: column; gap: 10px; width: 358px;">
|
|
137
|
+
<span :style="docLabelStyle">358px</span>
|
|
138
|
+
<CpFlightTabs :flights="sampleFlights" v-model:active-flight-id="activeFlightId" />
|
|
139
|
+
</div>
|
|
140
|
+
`,
|
|
141
|
+
}),
|
|
142
|
+
}
|