@citizenplane/pimp 18.19.0 → 18.19.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@citizenplane/pimp",
3
- "version": "18.19.0",
3
+ "version": "18.19.2",
4
4
  "scripts": {
5
5
  "dev": "storybook dev -p 8081",
6
6
  "build-storybook": "storybook build --output-dir ./docs",
@@ -1,7 +1,12 @@
1
1
  <template>
2
2
  <div class="cpAncillaryCategory">
3
- <div class="cpAncillaryCategory__header">
4
- <button class="cpAncillaryCategory__lead" type="button" @click="handleToggle">
3
+ <component
4
+ :is="headerTag"
5
+ v-bind="headerAttrs"
6
+ class="cpAncillaryCategory__header"
7
+ @click="handleHeaderClick"
8
+ >
9
+ <div class="cpAncillaryCategory__lead">
5
10
  <div class="cpAncillaryCategory__visual">
6
11
  <img
7
12
  v-if="visual"
@@ -12,34 +17,32 @@
12
17
  />
13
18
  <cp-icon v-else class="cpAncillaryCategory__fallbackIcon" size="40" type="shopping-bag" />
14
19
  </div>
15
- <div class="cpAncillaryCategory__texts">
16
- <span :id="titleId" class="cpAncillaryCategory__title">{{ title }}</span>
17
- <span class="cpAncillaryCategory__description">{{ description }}</span>
20
+ <div class="cpAncillaryCategory__content">
21
+ <div class="cpAncillaryCategory__texts">
22
+ <span :id="titleId" class="cpAncillaryCategory__title">{{ title }}</span>
23
+ <span class="cpAncillaryCategory__description">{{ description }}</span>
24
+ </div>
25
+ <div v-if="isSwitch" class="cpAncillaryCategory__trailing">
26
+ <span v-if="hasFromPrice" class="cpAncillaryCategory__price">{{ formattedPrice }}</span>
27
+ <cp-switch v-model="checked" color="accent" />
28
+ </div>
29
+ <div v-else-if="isActuallyExpandable" class="cpAncillaryCategory__trailing">
30
+ <span v-if="hasFromPrice" class="cpAncillaryCategory__fromPriceClip">
31
+ <span :aria-hidden="isOpen" class="cpAncillaryCategory__fromPrice" :class="fromPriceDynamicClass">
32
+ <span class="cpAncillaryCategory__fromPriceLabel">{{ fromPriceLabel }}</span>
33
+ <span class="cpAncillaryCategory__fromPriceAmount">{{ formattedPrice }}</span>
34
+ </span>
35
+ </span>
36
+ <cp-icon
37
+ class="cpAncillaryCategory__chevron"
38
+ :class="chevronDynamicClass"
39
+ size="24"
40
+ type="chevron-down"
41
+ />
42
+ </div>
18
43
  </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
44
  </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>
45
+ </component>
43
46
  <cp-transition-expand v-if="isActuallyExpandable">
44
47
  <div v-if="isOpen">
45
48
  <div :id="bodyId" class="cpAncillaryCategory__body">
@@ -98,6 +101,12 @@ const toggleId = useId()
98
101
  const titleId = computed(() => `${toggleId}-title`)
99
102
  const bodyId = computed(() => `${toggleId}-body`)
100
103
 
104
+ const headerTag = computed(() => (isActuallyExpandable.value ? 'button' : 'div'))
105
+
106
+ const headerAttrs = computed(() =>
107
+ isActuallyExpandable.value ? { type: 'button', 'aria-expanded': isOpen.value, 'aria-controls': bodyId.value } : {},
108
+ )
109
+
101
110
  const categoryVisuals: Partial<Record<CpAncillaryCategoryTypes, string>> = {
102
111
  [CpAncillaryCategoryTypes.BAGGAGES]: BaggagesVisual,
103
112
  [CpAncillaryCategoryTypes.CARBON_OFFSET]: CarbonOffsetVisual,
@@ -126,8 +135,8 @@ const fromPriceDynamicClass = computed(() => ({ 'cpAncillaryCategory__fromPrice-
126
135
 
127
136
  const chevronDynamicClass = computed(() => ({ 'cpAncillaryCategory__chevron--isRotated': isOpen.value }))
128
137
 
129
- const handleToggle = () => {
130
- isOpen.value = !isOpen.value
138
+ const handleHeaderClick = () => {
139
+ if (isActuallyExpandable.value) isOpen.value = !isOpen.value
131
140
  }
132
141
  </script>
133
142
 
@@ -141,15 +150,38 @@ const handleToggle = () => {
141
150
  border-radius: var(--cp-radius-xl);
142
151
  background: var(--cp-background-primary);
143
152
  box-shadow: var(--cp-shadows-xs);
153
+ container-type: inline-size;
144
154
  gap: var(--cp-spacing-sm);
145
155
 
146
156
  &__header {
157
+ @extend %u-focus-outline;
158
+
147
159
  display: flex;
148
160
  width: 100%;
149
- align-items: center;
150
161
  padding: var(--cp-spacing-lg);
151
162
  border-radius: var(--cp-radius-lg);
163
+ text-align: left;
164
+ }
165
+
166
+ &__lead {
167
+ display: flex;
168
+ width: 100%;
169
+ min-width: 0;
170
+ align-items: center;
171
+ gap: var(--cp-spacing-xl);
172
+ }
173
+
174
+ &__content {
175
+ display: flex;
176
+ min-width: 0;
177
+ flex: auto;
178
+ align-items: center;
152
179
  gap: var(--cp-spacing-lg);
180
+
181
+ @container (width < 30rem) {
182
+ flex-direction: column;
183
+ align-items: stretch;
184
+ }
153
185
  }
154
186
 
155
187
  &__trailing {
@@ -160,6 +192,11 @@ const handleToggle = () => {
160
192
  padding: 0 var(--cp-spacing-xl);
161
193
  border-radius: var(--cp-radius-lg);
162
194
  gap: var(--cp-spacing-lg);
195
+
196
+ @container (width < 30rem) {
197
+ justify-content: flex-start;
198
+ padding: 0;
199
+ }
163
200
  }
164
201
 
165
202
  &__price {
@@ -177,7 +214,7 @@ const handleToggle = () => {
177
214
  &__fromPrice {
178
215
  display: flex;
179
216
  align-items: center;
180
- gap: var(--cp-spacing-md);
217
+ gap: var(--cp-spacing-sm);
181
218
  opacity: 1;
182
219
  transform: translate3d(0, 0, 0);
183
220
  transition:
@@ -206,46 +243,6 @@ const handleToggle = () => {
206
243
  line-height: var(--cp-text-lg-line-height);
207
244
  }
208
245
 
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
246
  &__visual {
250
247
  display: flex;
251
248
  width: fn.px-to-rem(96);
@@ -273,6 +270,7 @@ const handleToggle = () => {
273
270
  &__texts {
274
271
  display: flex;
275
272
  min-width: 0;
273
+ flex: auto;
276
274
  flex-direction: column;
277
275
  }
278
276
 
@@ -297,6 +295,10 @@ const handleToggle = () => {
297
295
  &--isRotated {
298
296
  rotate: 180deg;
299
297
  }
298
+
299
+ @container (width < 30rem) {
300
+ display: none;
301
+ }
300
302
  }
301
303
 
302
304
  &__body {
@@ -305,7 +307,6 @@ const handleToggle = () => {
305
307
  padding: var(--cp-spacing-lg);
306
308
  border-radius: var(--cp-radius-lg);
307
309
  background: var(--cp-background-primary);
308
- gap: var(--cp-spacing-lg);
309
310
  }
310
311
  }
311
312
  </style>
@@ -1,5 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/vue3-vite'
2
- import { onMounted, ref } from 'vue'
2
+ import { ref } from 'vue'
3
3
 
4
4
  import CpAncillaryCategory from '@/components/CpAncillaryCategory.vue'
5
5
  import CpAncillaryItem from '@/components/CpAncillaryItem.vue'
@@ -14,17 +14,6 @@ const galleryRowStyle = 'display: flex; flex-direction: column; gap: 10px; width
14
14
  const meta = {
15
15
  title: 'Business/CpAncillaryCategory',
16
16
  component: CpAncillaryCategory,
17
- decorators: [
18
- (story) => ({
19
- components: { story },
20
- setup() {
21
- onMounted(() => {
22
- document.getElementById('storybook-root')?.style.setProperty('width', '100%')
23
- })
24
- },
25
- template: '<story />',
26
- }),
27
- ],
28
17
  argTypes: {
29
18
  currency: {
30
19
  control: 'text',
@@ -232,6 +221,56 @@ export const Switch: Story = {
232
221
  }),
233
222
  }
234
223
 
224
+ /**
225
+ * Same expandable and switch cards at a 358px container width: the
226
+ * container query on the root stacks the from-price under the texts
227
+ * instead of beside them.
228
+ */
229
+ export const Mobile: Story = {
230
+ args: {
231
+ ...Default.args,
232
+ },
233
+ parameters: { controls: { disable: true } },
234
+ render: () => ({
235
+ components: { CpAncillaryCategory },
236
+ setup() {
237
+ const switchChecked = ref(false)
238
+ return { galleryListStyle, galleryRowStyle, docLabelStyle, switchChecked }
239
+ },
240
+ template: `
241
+ <div :style="galleryListStyle">
242
+ <div :style="galleryRowStyle">
243
+ <span :style="docLabelStyle">Expandable</span>
244
+ <div style="width: 358px;">
245
+ <CpAncillaryCategory
246
+ title="Sport Equipments"
247
+ description="Travel with special or oversized items you need to bring along."
248
+ type="sportEquipments"
249
+ is-expandable
250
+ is-default-closed
251
+ :from-price="5990"
252
+ currency="EUR"
253
+ />
254
+ </div>
255
+ </div>
256
+ <div :style="galleryRowStyle">
257
+ <span :style="docLabelStyle">Switch</span>
258
+ <div style="width: 358px;">
259
+ <CpAncillaryCategory
260
+ v-model:checked="switchChecked"
261
+ title="Carbon offset"
262
+ description="Compensate the CO2 emissions of your flight"
263
+ type="carbonOffset"
264
+ :from-price="490"
265
+ currency="EUR"
266
+ />
267
+ </div>
268
+ </div>
269
+ </div>
270
+ `,
271
+ }),
272
+ }
273
+
235
274
  /* -------------------------------------------------------------------------- */
236
275
  /* Gallery */
237
276
  /* -------------------------------------------------------------------------- */