@africanies/angular-web-sdk 0.1.7 → 0.1.9
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/.angular/cache/22.0.9/playground/.tsbuildinfo +1 -1
- package/.nx/cache/{4082520855419352840 → 15302120112746851896}/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +30 -8
- package/.nx/cache/15302120112746851896/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -0
- package/.nx/cache/{4082520855419352840 → 15302120112746851896}/dist/libs/africanies-theme/tailwind-preset.cjs +89 -12
- package/.nx/cache/{4082520855419352840 → 15302120112746851896}/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +14 -4
- package/.nx/cache/17289624717336768573/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +4639 -0
- package/.nx/cache/17289624717336768573/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +1 -0
- package/.nx/cache/17289624717336768573/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +2748 -0
- package/.nx/cache/18393832731522001121/dist/libs/africanies-core/README.md +85 -0
- package/.nx/cache/{17491371155400585077 → 18393832731522001121}/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +139 -51
- package/.nx/cache/18393832731522001121/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +1 -0
- package/.nx/cache/18393832731522001121/dist/libs/africanies-core/package.json +31 -0
- package/.nx/cache/{17491371155400585077 → 18393832731522001121}/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +14 -11
- package/.nx/cache/{3314955264390318442 → 3953970051755345887}/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +775 -566
- package/.nx/cache/3953970051755345887/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -0
- package/.nx/cache/{17499961385238663835 → 3953970051755345887}/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +150 -119
- package/.nx/cache/{12276756491762927325 → 4225324930345532464}/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +34 -6
- package/.nx/cache/4225324930345532464/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -0
- package/.nx/cache/{12276756491762927325 → 4225324930345532464}/dist/libs/africanies-theme/tailwind-preset.cjs +104 -19
- package/.nx/cache/{12276756491762927325 → 4225324930345532464}/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +21 -2
- package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/README.md +69 -0
- package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/assets/icons.sprite.svg +1851 -0
- package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs +833 -0
- package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs.map +1 -0
- package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/package.json +30 -0
- package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/types/africanies-africanies-icons.d.ts +108 -0
- package/.nx/cache/877026836685850193/dist/libs/africanies-models/README.md +60 -0
- package/.nx/cache/877026836685850193/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs +613 -0
- package/.nx/cache/877026836685850193/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs.map +1 -0
- package/.nx/cache/877026836685850193/dist/libs/africanies-models/package.json +23 -0
- package/.nx/cache/877026836685850193/dist/libs/africanies-models/types/africanies-africanies-models.d.ts +1452 -0
- package/.nx/cache/{17499961385238663835 → 9406544675747598148}/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +765 -615
- package/.nx/cache/9406544675747598148/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -0
- package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +170 -130
- package/.nx/cache/run.json +36 -36
- package/.nx/cache/terminalOutputs/{3017748574492080228 → 12565224028063674529} +16 -7
- package/.nx/cache/terminalOutputs/{8648260256613557249 → 13478445034325959094} +175 -172
- package/.nx/cache/terminalOutputs/{7126777490381568487 → 15149466893522345489} +10 -5
- package/.nx/cache/terminalOutputs/{4082520855419352840 → 15302120112746851896} +1 -1
- package/.nx/cache/terminalOutputs/15835338512221847842 +185 -0
- package/.nx/cache/terminalOutputs/{17491371155400585077 → 17289624717336768573} +1 -1
- package/.nx/cache/terminalOutputs/17571479482482478257 +35 -0
- package/.nx/cache/terminalOutputs/{9408529484792607601 → 17960163310445607219} +1 -1
- package/.nx/cache/terminalOutputs/18393832731522001121 +21 -0
- package/.nx/cache/terminalOutputs/3088264223473143358 +8 -0
- package/.nx/cache/terminalOutputs/{3314955264390318442 → 3953970051755345887} +1 -1
- package/.nx/cache/terminalOutputs/{12276756491762927325 → 4225324930345532464} +1 -1
- package/.nx/cache/terminalOutputs/4363587767476846758 +176 -0
- package/.nx/cache/terminalOutputs/5151121439688073678 +21 -0
- package/.nx/cache/terminalOutputs/7462873189468732296 +17 -0
- package/.nx/cache/terminalOutputs/8032148447904822353 +184 -182
- package/.nx/cache/terminalOutputs/8193462679279018087 +39 -0
- package/.nx/cache/terminalOutputs/877026836685850193 +21 -0
- package/.nx/cache/terminalOutputs/9406544675747598148 +21 -0
- package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db +0 -0
- package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-shm +0 -0
- package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-wal +0 -0
- package/.nx/workspace-data/d/daemon.log +6813 -0
- package/.nx/workspace-data/d/server-process.json +2 -2
- package/.nx/workspace-data/file-map.json +3490 -3432
- package/.nx/workspace-data/nx_files.nxt +0 -0
- package/.nx/workspace-data/project-graph.json +7 -2
- package/apps/playground/src/app/pages/tokens-page.ts +17 -13
- package/apps/playground/src/app/snippets/filters.snippets.ts +10 -0
- package/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +6 -1
- package/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +1 -1
- package/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +6 -1
- package/dist/libs/africanies-icons/assets/icons.sprite.svg +7 -0
- package/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs +1 -0
- package/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs.map +1 -1
- package/dist/libs/africanies-icons/types/africanies-africanies-icons.d.ts +2 -2
- package/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs +22 -8
- package/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs.map +1 -1
- package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +22 -10
- package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -1
- package/dist/libs/africanies-theme/tailwind-preset.cjs +84 -16
- package/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +4 -2
- package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +723 -618
- package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -1
- package/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +142 -114
- package/libs/africanies-core/src/index.ts +1 -1
- package/libs/africanies-core/src/lib/notification/notification-link.ts +6 -1
- package/libs/africanies-icons/src/assets/icons.sprite.svg +7 -0
- package/libs/africanies-icons/src/lib/icon-name.ts +1 -0
- package/libs/africanies-models/src/lib/filters/configs/seed-configs.ts +22 -8
- package/libs/africanies-theme/src/lib/mode-color.safelist.ts +12 -0
- package/libs/africanies-theme/src/lib/mode-color.service.spec.ts +55 -0
- package/libs/africanies-theme/src/lib/mode-color.service.ts +11 -11
- package/libs/africanies-theme/tailwind-preset.cjs +84 -16
- package/libs/africanies-ui/src/lib/action-menu/action-menu.component.ts +3 -3
- package/libs/africanies-ui/src/lib/alert/alert.component.ts +4 -4
- package/libs/africanies-ui/src/lib/avatar/avatar-menu.component.ts +2 -2
- package/libs/africanies-ui/src/lib/button/button.component.spec.ts +7 -7
- package/libs/africanies-ui/src/lib/button/button.component.ts +5 -5
- package/libs/africanies-ui/src/lib/chip/chip.component.ts +4 -4
- package/libs/africanies-ui/src/lib/content-stack/content-stack.component.ts +1 -1
- package/libs/africanies-ui/src/lib/feedback/error-indicator.component.ts +3 -3
- package/libs/africanies-ui/src/lib/feedback/error-state.component.ts +2 -2
- package/libs/africanies-ui/src/lib/filters/filter-drawer.panel.ts +44 -27
- package/libs/africanies-ui/src/lib/forms/checkbox/checkbox.component.ts +2 -1
- package/libs/africanies-ui/src/lib/forms/file-upload/file-preview.dialog.ts +1 -1
- package/libs/africanies-ui/src/lib/forms/file-upload/file-upload.component.ts +1 -1
- package/libs/africanies-ui/src/lib/forms/form-field.classes.ts +36 -6
- package/libs/africanies-ui/src/lib/forms/otp-input/otp-input.component.ts +2 -1
- package/libs/africanies-ui/src/lib/forms/radio/radio.component.ts +2 -1
- package/libs/africanies-ui/src/lib/forms/select/select.component.ts +1 -1
- package/libs/africanies-ui/src/lib/forms/toggle/toggle.component.ts +1 -1
- package/libs/africanies-ui/src/lib/image/image.component.ts +1 -1
- package/libs/africanies-ui/src/lib/info-popover/info-popover.component.ts +2 -2
- package/libs/africanies-ui/src/lib/layout/app-shell-content-header.component.ts +1 -1
- package/libs/africanies-ui/src/lib/layout/app-shell-header.component.ts +14 -14
- package/libs/africanies-ui/src/lib/layout/app-shell.component.html +2 -2
- package/libs/africanies-ui/src/lib/layout/app-shell.component.ts +3 -3
- package/libs/africanies-ui/src/lib/layout/header-greeting.util.ts +0 -2
- package/libs/africanies-ui/src/lib/layout/header-weather.spec.ts +11 -40
- package/libs/africanies-ui/src/lib/layout/header-weather.ts +22 -63
- package/libs/africanies-ui/src/lib/navigation/shipping-mode-switch/shipping-mode-switch.component.ts +6 -6
- package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.html +21 -13
- package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.ts +13 -7
- package/libs/africanies-ui/src/lib/notifications/notification-drawer.panel.ts +6 -6
- package/libs/africanies-ui/src/lib/overlay/confirm-dialog.component.ts +2 -2
- package/libs/africanies-ui/src/lib/overlay/drawer.service.ts +2 -2
- package/libs/africanies-ui/src/lib/overlay/modal.service.ts +3 -3
- package/libs/africanies-ui/src/lib/overlay/overlay-frame.component.ts +1 -1
- package/libs/africanies-ui/src/lib/pagination/pagination.component.ts +1 -1
- package/libs/africanies-ui/src/lib/stepper/stepper.component.ts +7 -7
- package/libs/africanies-ui/src/lib/table/table-column.ts +9 -0
- package/libs/africanies-ui/src/lib/table/table.component.spec.ts +99 -0
- package/libs/africanies-ui/src/lib/table/table.component.ts +72 -30
- package/libs/africanies-ui/src/lib/toast/toast-item.component.ts +6 -6
- package/package.json +1 -1
- package/svg/hand-coins.svg +9 -0
- package/.nx/cache/12276756491762927325/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +0 -1
- package/.nx/cache/17491371155400585077/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +0 -1
- package/.nx/cache/17499961385238663835/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +0 -1
- package/.nx/cache/3314955264390318442/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +0 -1
- package/.nx/cache/4082520855419352840/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +0 -1
- package/.nx/cache/terminalOutputs/17499961385238663835 +0 -21
- /package/.nx/cache/{12276756491762927325 → 15302120112746851896}/dist/libs/africanies-theme/README.md +0 -0
- /package/.nx/cache/{12276756491762927325 → 15302120112746851896}/dist/libs/africanies-theme/package.json +0 -0
- /package/.nx/cache/{17491371155400585077 → 17289624717336768573}/dist/libs/africanies-core/README.md +0 -0
- /package/.nx/cache/{17491371155400585077 → 17289624717336768573}/dist/libs/africanies-core/package.json +0 -0
- /package/.nx/cache/{17499961385238663835 → 3953970051755345887}/dist/libs/africanies-ui/README.md +0 -0
- /package/.nx/cache/{17499961385238663835 → 3953970051755345887}/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
- /package/.nx/cache/{17499961385238663835 → 3953970051755345887}/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
- /package/.nx/cache/{17499961385238663835 → 3953970051755345887}/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +0 -0
- /package/.nx/cache/{17499961385238663835 → 3953970051755345887}/dist/libs/africanies-ui/assets/carriers/dhl.svg +0 -0
- /package/.nx/cache/{17499961385238663835 → 3953970051755345887}/dist/libs/africanies-ui/package.json +0 -0
- /package/.nx/cache/{4082520855419352840 → 4225324930345532464}/dist/libs/africanies-theme/README.md +0 -0
- /package/.nx/cache/{4082520855419352840 → 4225324930345532464}/dist/libs/africanies-theme/package.json +0 -0
- /package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/README.md +0 -0
- /package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
- /package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
- /package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +0 -0
- /package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/assets/carriers/dhl.svg +0 -0
- /package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/package.json +0 -0
- /package/.nx/cache/terminalOutputs/{16279119293212733518 → 14005735098959303198} +0 -0
|
@@ -35,7 +35,7 @@ import { ChangeDetectionStrategy, Component, input } from '@angular/core';
|
|
|
35
35
|
</p>
|
|
36
36
|
}
|
|
37
37
|
@if (extraLine()) {
|
|
38
|
-
<p class="m-0 text-caption text-neutral-
|
|
38
|
+
<p class="m-0 text-caption text-neutral-600 dark:text-neutral-400">
|
|
39
39
|
{{ extraLine() }}
|
|
40
40
|
</p>
|
|
41
41
|
}
|
|
@@ -132,10 +132,10 @@ import { ButtonComponent } from '../button/button.component';
|
|
|
132
132
|
aria-hidden="true"
|
|
133
133
|
>
|
|
134
134
|
<span
|
|
135
|
-
class="africanies-error-indicator-dot-ping absolute inset-0 rounded-full bg-
|
|
135
|
+
class="africanies-error-indicator-dot-ping absolute inset-0 rounded-full bg-danger-dark opacity-75"
|
|
136
136
|
></span>
|
|
137
137
|
<span
|
|
138
|
-
class="africanies-error-indicator-dot-core relative size-3 rounded-full bg-
|
|
138
|
+
class="africanies-error-indicator-dot-core relative size-3 rounded-full bg-danger-dark"
|
|
139
139
|
></span>
|
|
140
140
|
</span>
|
|
141
141
|
|
|
@@ -150,7 +150,7 @@ import { ButtonComponent } from '../button/button.component';
|
|
|
150
150
|
type="button"
|
|
151
151
|
variant="ghost"
|
|
152
152
|
size="sm"
|
|
153
|
-
class="africanies-error-indicator-retry shrink-0 !min-h-0 !border-transparent !bg-transparent !px-1.5 !py-0.5 !text-danger hover:!bg-danger/10 dark:hover:!bg-danger/20"
|
|
153
|
+
class="africanies-error-indicator-retry shrink-0 !min-h-0 !border-transparent !bg-transparent !px-1.5 !py-0.5 !text-danger-dark hover:!bg-danger/10 dark:hover:!bg-danger/20"
|
|
154
154
|
[class.opacity-50]="disabled()"
|
|
155
155
|
[disabled]="disabled()"
|
|
156
156
|
(click)="onRetry()"
|
|
@@ -35,8 +35,8 @@ import { ButtonComponent } from '../button/button.component';
|
|
|
35
35
|
class="flex flex-col items-center justify-center gap-3 py-10 px-4 text-center text-ink dark:text-white"
|
|
36
36
|
role="alert"
|
|
37
37
|
>
|
|
38
|
-
<africanies-icon name="warning" [size]="32" class="text-danger" />
|
|
39
|
-
<p class="m-0 text-body text-danger max-w-md">{{ message() }}</p>
|
|
38
|
+
<africanies-icon name="warning" [size]="32" class="text-danger-dark dark:text-danger" />
|
|
39
|
+
<p class="m-0 text-body text-danger-dark max-w-md dark:text-danger">{{ message() }}</p>
|
|
40
40
|
<button
|
|
41
41
|
africanies-button
|
|
42
42
|
type="button"
|
|
@@ -26,10 +26,6 @@ import {
|
|
|
26
26
|
|
|
27
27
|
import { ButtonComponent } from '../button/button.component';
|
|
28
28
|
import { DatePickerComponent } from '../forms/date-picker/date-picker.component';
|
|
29
|
-
import {
|
|
30
|
-
RadioComponent,
|
|
31
|
-
type RadioOption,
|
|
32
|
-
} from '../forms/radio/radio.component';
|
|
33
29
|
import { SelectComponent } from '../forms/select/select.component';
|
|
34
30
|
import type { SelectOption } from '../forms/select/select.types';
|
|
35
31
|
import { TextInputComponent } from '../forms/text-input/text-input.component';
|
|
@@ -62,7 +58,6 @@ import { FilterQueryService } from './filter-query.service';
|
|
|
62
58
|
AfricaniesIconComponent,
|
|
63
59
|
ButtonComponent,
|
|
64
60
|
DatePickerComponent,
|
|
65
|
-
RadioComponent,
|
|
66
61
|
SelectComponent,
|
|
67
62
|
TextInputComponent,
|
|
68
63
|
],
|
|
@@ -220,12 +215,37 @@ import { FilterQueryService } from './filter-query.service';
|
|
|
220
215
|
</div>
|
|
221
216
|
}
|
|
222
217
|
@case ('boolean') {
|
|
223
|
-
<
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
218
|
+
<div class="flex flex-wrap gap-2">
|
|
219
|
+
@for (opt of booleanOptions(field); track opt.value) {
|
|
220
|
+
<button
|
|
221
|
+
type="button"
|
|
222
|
+
class="cursor-pointer rounded-md border px-2.5 py-1.5 text-body-sm transition-colors"
|
|
223
|
+
[style.color]="
|
|
224
|
+
draft().values[field.key] === opt.value
|
|
225
|
+
? opt.color ?? null
|
|
226
|
+
: null
|
|
227
|
+
"
|
|
228
|
+
[style.border-color]="
|
|
229
|
+
draft().values[field.key] === opt.value
|
|
230
|
+
? opt.color ?? null
|
|
231
|
+
: null
|
|
232
|
+
"
|
|
233
|
+
[style.background-color]="
|
|
234
|
+
draft().values[field.key] === opt.value && opt.color
|
|
235
|
+
? opt.color + '1A'
|
|
236
|
+
: null
|
|
237
|
+
"
|
|
238
|
+
[class]="
|
|
239
|
+
draft().values[field.key] === opt.value
|
|
240
|
+
? 'font-medium'
|
|
241
|
+
: 'border-neutral-300 text-neutral-600 hover:border-neutral-400 dark:border-white/15 dark:text-neutral-300'
|
|
242
|
+
"
|
|
243
|
+
(click)="setFieldValue(field.key, opt.value)"
|
|
244
|
+
>
|
|
245
|
+
{{ opt.label }}
|
|
246
|
+
</button>
|
|
247
|
+
}
|
|
248
|
+
</div>
|
|
229
249
|
}
|
|
230
250
|
@case ('select') {
|
|
231
251
|
<!-- Label already shown in the section header — avoid double label. -->
|
|
@@ -284,7 +304,7 @@ import { FilterQueryService } from './filter-query.service';
|
|
|
284
304
|
type="button"
|
|
285
305
|
variant="ghost"
|
|
286
306
|
size="sm"
|
|
287
|
-
class="shrink-0 !min-h-0 self-start !px-1 !py-0 text-neutral-
|
|
307
|
+
class="shrink-0 !min-h-0 self-start !px-1 !py-0 text-neutral-600 hover:text-ink dark:text-neutral-400 dark:hover:text-white"
|
|
288
308
|
aria-label="Dismiss"
|
|
289
309
|
(click)="applyError.set(null)"
|
|
290
310
|
>
|
|
@@ -507,14 +527,6 @@ export class FilterDrawerPanel {
|
|
|
507
527
|
this.draft.update((s) => ({ ...s, order: opt?.value }));
|
|
508
528
|
}
|
|
509
529
|
|
|
510
|
-
protected onRadioChange(key: string, value: string | null): void {
|
|
511
|
-
if (value == null) {
|
|
512
|
-
this.clearField(key);
|
|
513
|
-
return;
|
|
514
|
-
}
|
|
515
|
-
this.setFieldValue(key, value);
|
|
516
|
-
}
|
|
517
|
-
|
|
518
530
|
protected onSelectChange(
|
|
519
531
|
key: string,
|
|
520
532
|
selected: SelectOption<string> | SelectOption<string>[] | null,
|
|
@@ -678,13 +690,18 @@ export class FilterDrawerPanel {
|
|
|
678
690
|
);
|
|
679
691
|
}
|
|
680
692
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
693
|
+
/**
|
|
694
|
+
* Boolean Yes/No chips. Prefer field.options when present; otherwise default
|
|
695
|
+
* colored Yes/No so every boolean filter stays visually distinct.
|
|
696
|
+
*/
|
|
697
|
+
protected booleanOptions(field: FilterFieldModel): FilterOptionModel[] {
|
|
698
|
+
if (field.options?.length) {
|
|
699
|
+
return field.options;
|
|
700
|
+
}
|
|
701
|
+
return [
|
|
702
|
+
{ value: '1', label: 'Yes', color: '#25945c' },
|
|
703
|
+
{ value: '0', label: 'No', color: '#FF001C' },
|
|
704
|
+
];
|
|
688
705
|
}
|
|
689
706
|
|
|
690
707
|
protected onReset(): void {
|
|
@@ -19,6 +19,7 @@ import { ModeColorService } from '@africanies/africanies-theme';
|
|
|
19
19
|
|
|
20
20
|
import {
|
|
21
21
|
FORM_ERROR_CLASS,
|
|
22
|
+
FORM_FOCUS_VISIBLE_PEER_CLASS,
|
|
22
23
|
FORM_HINT_CLASS,
|
|
23
24
|
} from '../form-field.classes';
|
|
24
25
|
|
|
@@ -132,7 +133,7 @@ export class CheckboxComponent implements ControlValueAccessor {
|
|
|
132
133
|
protected readonly markClass = computed(() => {
|
|
133
134
|
const base =
|
|
134
135
|
'pointer-events-none flex size-4 items-center justify-center rounded border text-white transition-colors ' +
|
|
135
|
-
|
|
136
|
+
FORM_FOCUS_VISIBLE_PEER_CLASS;
|
|
136
137
|
const err = this.error() ? ' !border-danger dark:!border-danger' : '';
|
|
137
138
|
if (this.value()) {
|
|
138
139
|
const c = this.modeColor.classes();
|
|
@@ -83,7 +83,7 @@ type PreviewKind = 'image' | 'unavailable';
|
|
|
83
83
|
<div
|
|
84
84
|
class="flex flex-col items-center gap-3 rounded-md bg-background-welcome px-6 py-10 text-center ring-1 ring-border dark:bg-white/5 dark:ring-white/15"
|
|
85
85
|
>
|
|
86
|
-
<africanies-icon name="eye-slash" [size]="32" class="text-neutral-
|
|
86
|
+
<africanies-icon name="eye-slash" [size]="32" class="text-neutral-600" />
|
|
87
87
|
<p class="m-0 text-body font-medium text-ink dark:text-white">
|
|
88
88
|
Preview not available
|
|
89
89
|
</p>
|
|
@@ -341,7 +341,7 @@ let nextFileUploadId = 0;
|
|
|
341
341
|
<span> · </span>
|
|
342
342
|
<button
|
|
343
343
|
type="button"
|
|
344
|
-
class="cursor-pointer bg-transparent p-0 text-caption font-medium text-ink underline underline-offset-2 hover:text-neutral-700 focus-visible:outline focus-visible:
|
|
344
|
+
class="cursor-pointer bg-transparent p-0 text-caption font-medium text-ink underline underline-offset-2 hover:text-neutral-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ink dark:text-white dark:hover:text-neutral-200"
|
|
345
345
|
(click)="openPreview(item); $event.stopPropagation()"
|
|
346
346
|
>
|
|
347
347
|
View
|
|
@@ -6,29 +6,59 @@
|
|
|
6
6
|
* when consumers scan the published UI bundle.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Inset focus ring — stays inside the control border box.
|
|
11
|
+
*
|
|
12
|
+
* Prefer this over `outline` + `outline-offset`. Those paint *outside* the box
|
|
13
|
+
* and are clipped by `overflow: hidden|clip|auto` ancestors (modals, drawers,
|
|
14
|
+
* steppers, scroll panes). `box-sizing` does not affect outlines.
|
|
15
|
+
*/
|
|
16
|
+
export const FORM_FOCUS_WITHIN_CLASS =
|
|
17
|
+
'focus-within:outline-none focus-within:ring-2 focus-within:ring-inset focus-within:ring-focus';
|
|
18
|
+
|
|
19
|
+
/** Same inset ring for host / button-style focus-visible. */
|
|
20
|
+
export const FORM_FOCUS_VISIBLE_CLASS =
|
|
21
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus';
|
|
22
|
+
|
|
23
|
+
/** Peer-driven inset ring (checkbox / radio control chrome). */
|
|
24
|
+
export const FORM_FOCUS_VISIBLE_PEER_CLASS =
|
|
25
|
+
'peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-inset peer-focus-visible:ring-focus';
|
|
26
|
+
|
|
27
|
+
/** Direct focus (OTP cells and similar). */
|
|
28
|
+
export const FORM_FOCUS_CLASS =
|
|
29
|
+
'focus:outline-none focus:ring-2 focus:ring-inset focus:ring-focus';
|
|
30
|
+
|
|
9
31
|
/** Label above the control. Hidden when empty so callers can omit a label. */
|
|
10
32
|
export const FORM_LABEL_CLASS =
|
|
11
33
|
'block text-body-sm font-medium text-ink dark:text-white mb-1.5 empty:hidden';
|
|
12
34
|
|
|
13
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Default control shell — `h-10` matches md `africanies-button`.
|
|
37
|
+
*
|
|
38
|
+
* Focus utilities are inlined (not concatenated) so Tailwind content scanning
|
|
39
|
+
* always sees the full class names in this string literal.
|
|
40
|
+
*/
|
|
14
41
|
export const FORM_FIELD_CLASS =
|
|
15
|
-
'flex items-center w-full h-10 box-border rounded-
|
|
16
|
-
'focus-within:outline focus-within:
|
|
42
|
+
'flex items-center w-full h-10 box-border rounded-control border border-control bg-surface dark:bg-ink-950 text-ink dark:text-white dark:border-white/25 ' +
|
|
43
|
+
'focus-within:outline-none focus-within:ring-2 focus-within:ring-inset focus-within:ring-focus';
|
|
17
44
|
|
|
18
45
|
/**
|
|
19
46
|
* Error border — `!` so it wins over shell `border-border` /
|
|
20
47
|
* `dark:border-white/25` (same specificity; those utilities sort later in the
|
|
21
48
|
* generated CSS and otherwise paint over `border-danger`).
|
|
22
49
|
*
|
|
50
|
+
* Use `danger` (`#ff001c`) for both border and focus ring so field chrome
|
|
51
|
+
* matches validation copy (not the darker accessible `danger-dark` text token).
|
|
52
|
+
*
|
|
23
53
|
* Keep these literals static for Tailwind content scanning in consumer apps.
|
|
24
54
|
*/
|
|
25
55
|
export const FORM_FIELD_ERROR_CLASS =
|
|
26
|
-
'!border-danger dark:!border-danger focus-within:!
|
|
56
|
+
'!border-danger dark:!border-danger focus-within:!ring-danger focus:!ring-danger';
|
|
27
57
|
|
|
28
58
|
/** Native input / textarea inside a shell. */
|
|
29
59
|
export const FORM_CONTROL_INNER_CLASS =
|
|
30
60
|
'flex-1 min-w-0 min-h-0 h-full w-full bg-transparent border-0 outline-none text-body text-ink dark:text-white px-3 py-0 ' +
|
|
31
|
-
'placeholder:text-neutral-
|
|
61
|
+
'placeholder:text-neutral-500 disabled:cursor-not-allowed';
|
|
32
62
|
|
|
33
63
|
/** Prefix / suffix projection gutters. Hidden when nothing is projected. */
|
|
34
64
|
export const FORM_AFFIX_CLASS =
|
|
@@ -50,7 +80,7 @@ export const FORM_DATE_INNER_CLASS =
|
|
|
50
80
|
export const FORM_HINT_CLASS =
|
|
51
81
|
'mt-1.5 text-caption text-neutral-600 dark:text-neutral-400 m-0';
|
|
52
82
|
|
|
53
|
-
/** Field-level validation message (not ErrorStateComponent). */
|
|
83
|
+
/** Field-level validation message (not ErrorStateComponent). Matches `#ff001c`. */
|
|
54
84
|
export const FORM_ERROR_CLASS = 'mt-1.5 text-caption text-danger m-0';
|
|
55
85
|
|
|
56
86
|
/** Disabled shell — dimmed with not-allowed cursor over the full field chrome. */
|
|
@@ -24,6 +24,7 @@ import { ButtonComponent } from '../../button/button.component';
|
|
|
24
24
|
import {
|
|
25
25
|
FORM_ERROR_CLASS,
|
|
26
26
|
FORM_FIELD_ERROR_CLASS,
|
|
27
|
+
FORM_FOCUS_CLASS,
|
|
27
28
|
FORM_HINT_CLASS,
|
|
28
29
|
FORM_LABEL_CLASS,
|
|
29
30
|
} from '../form-field.classes';
|
|
@@ -165,7 +166,7 @@ export class OtpInputComponent implements ControlValueAccessor, OnDestroy {
|
|
|
165
166
|
let classes =
|
|
166
167
|
'size-10 shrink-0 rounded-md border border-neutral-300 bg-white text-center text-body font-medium text-ink outline-none transition-colors ' +
|
|
167
168
|
'dark:border-white/25 dark:bg-ink-950 dark:text-white ' +
|
|
168
|
-
|
|
169
|
+
`${FORM_FOCUS_CLASS} disabled:cursor-not-allowed`;
|
|
169
170
|
if (this.masked()) {
|
|
170
171
|
classes += ' africanies-otp-cell-masked text-body-lg';
|
|
171
172
|
}
|
|
@@ -18,6 +18,7 @@ import { ModeColorService } from '@africanies/africanies-theme';
|
|
|
18
18
|
|
|
19
19
|
import {
|
|
20
20
|
FORM_ERROR_CLASS,
|
|
21
|
+
FORM_FOCUS_VISIBLE_PEER_CLASS,
|
|
21
22
|
FORM_HINT_CLASS,
|
|
22
23
|
FORM_LABEL_CLASS,
|
|
23
24
|
} from '../form-field.classes';
|
|
@@ -178,7 +179,7 @@ export class RadioComponent<T = string> implements ControlValueAccessor {
|
|
|
178
179
|
protected markClass(opt: RadioOption<T>): string {
|
|
179
180
|
const base =
|
|
180
181
|
'pointer-events-none flex size-4 items-center justify-center rounded-full border-2 bg-transparent transition-colors ' +
|
|
181
|
-
|
|
182
|
+
FORM_FOCUS_VISIBLE_PEER_CLASS;
|
|
182
183
|
const err = this.error() ? ' !border-danger dark:!border-danger' : '';
|
|
183
184
|
if (this.isSelected(opt)) {
|
|
184
185
|
// Ring uses mode accent; fill stays transparent so the center dot reads clearly.
|
|
@@ -305,7 +305,7 @@ const SELECT_PANEL_POSITIONS: ConnectedPosition[] = [
|
|
|
305
305
|
<input
|
|
306
306
|
#searchInput
|
|
307
307
|
type="search"
|
|
308
|
-
class="w-full rounded-md border border-neutral-300 dark:border-white/25 bg-transparent text-body text-ink dark:text-white px-2.5 py-1.5 outline-none focus-visible:outline focus-visible:
|
|
308
|
+
class="w-full rounded-md border border-neutral-300 dark:border-white/25 bg-transparent text-body text-ink dark:text-white px-2.5 py-1.5 outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ink"
|
|
309
309
|
[value]="searchQuery()"
|
|
310
310
|
placeholder="Search…"
|
|
311
311
|
(input)="onSearchInput($event)"
|
|
@@ -71,7 +71,7 @@ let nextToggleId = 0;
|
|
|
71
71
|
[id]="controlId"
|
|
72
72
|
type="button"
|
|
73
73
|
role="switch"
|
|
74
|
-
class="relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border border-border p-0.5 box-border transition-colors focus-visible:outline focus-visible:
|
|
74
|
+
class="relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border border-border p-0.5 box-border transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ink dark:border-white/15"
|
|
75
75
|
[ngClass]="trackClass()"
|
|
76
76
|
[attr.aria-checked]="on()"
|
|
77
77
|
[attr.aria-busy]="loading() ? true : null"
|
|
@@ -70,7 +70,7 @@ export type ImageFit = 'cover' | 'contain';
|
|
|
70
70
|
|
|
71
71
|
@if (showPlaceholder()) {
|
|
72
72
|
<div
|
|
73
|
-
class="absolute inset-0 flex items-center justify-center bg-neutral-200 text-neutral-
|
|
73
|
+
class="absolute inset-0 flex items-center justify-center bg-neutral-200 text-neutral-600 dark:bg-white/10 dark:text-neutral-400"
|
|
74
74
|
[class.animate-pulse]="pending()"
|
|
75
75
|
aria-hidden="true"
|
|
76
76
|
>
|
|
@@ -245,9 +245,9 @@ export class InfoPopoverComponent {
|
|
|
245
245
|
*/
|
|
246
246
|
protected readonly panelArrowClass = computed(() => {
|
|
247
247
|
const card =
|
|
248
|
-
'relative z-10 w-max max-w-xs rounded-
|
|
248
|
+
'relative z-10 w-max max-w-xs rounded-panel border border-border bg-surface px-4 py-3 text-ink shadow-floating dark:border-white/15 dark:bg-ink-950 dark:text-white';
|
|
249
249
|
const base =
|
|
250
|
-
"before:pointer-events-none before:absolute before:z-0 before:h-2.5 before:w-2.5 before:rotate-45 before:border before:border-border before:bg-
|
|
250
|
+
"before:pointer-events-none before:absolute before:z-0 before:h-2.5 before:w-2.5 before:rotate-45 before:border before:border-border before:bg-surface before:content-[''] dark:before:border-white/15 dark:before:bg-ink-950";
|
|
251
251
|
switch (this.placement()) {
|
|
252
252
|
case 'bottom':
|
|
253
253
|
return `${card} ${base} before:left-1/2 before:top-0 before:-translate-x-1/2 before:-translate-y-1/2 before:border-b-0 before:border-r-0`;
|
|
@@ -39,7 +39,7 @@ import { PageHeaderComponent } from './page-header.component';
|
|
|
39
39
|
<div class="flex min-w-0 items-center gap-2 sm:gap-3">
|
|
40
40
|
@if (showBackButton()) {
|
|
41
41
|
<a
|
|
42
|
-
class="inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-lg text-neutral-600 transition-colors hover:bg-background-
|
|
42
|
+
class="inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-lg text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
|
|
43
43
|
[attr.aria-label]="backLabel()"
|
|
44
44
|
[routerLink]="backTarget()!.routerLink"
|
|
45
45
|
[queryParams]="backTarget()!.queryParams"
|
|
@@ -81,7 +81,7 @@ export class AppShellHeaderEndDirective {}
|
|
|
81
81
|
@if (greeting(); as hello) {
|
|
82
82
|
<div class="min-w-0 overflow-hidden">
|
|
83
83
|
<p
|
|
84
|
-
class="m-0 truncate text-caption leading-none text-neutral-
|
|
84
|
+
class="m-0 truncate text-caption leading-none text-neutral-600 dark:text-neutral-400"
|
|
85
85
|
>
|
|
86
86
|
{{ hello.kicker }}
|
|
87
87
|
</p>
|
|
@@ -107,19 +107,19 @@ export class AppShellHeaderEndDirective {}
|
|
|
107
107
|
<africanies-icon
|
|
108
108
|
[name]="weatherIcon()"
|
|
109
109
|
[size]="18"
|
|
110
|
-
class="text-neutral-
|
|
110
|
+
class="text-neutral-600 dark:text-neutral-400"
|
|
111
111
|
/>
|
|
112
112
|
@if (weatherTemp(); as temp) {
|
|
113
113
|
<span
|
|
114
114
|
class="text-body-sm font-medium tabular-nums tracking-tight text-ink dark:text-white"
|
|
115
115
|
>
|
|
116
|
-
{{ temp }}°
|
|
116
|
+
{{ temp }}°C
|
|
117
117
|
</span>
|
|
118
118
|
}
|
|
119
119
|
<span
|
|
120
|
-
class="hidden text-caption text-neutral-
|
|
120
|
+
class="hidden text-caption text-neutral-600 md:inline dark:text-neutral-400"
|
|
121
121
|
>
|
|
122
|
-
{{
|
|
122
|
+
{{ weatherCondition() }}
|
|
123
123
|
</span>
|
|
124
124
|
</div>
|
|
125
125
|
}
|
|
@@ -127,7 +127,7 @@ export class AppShellHeaderEndDirective {}
|
|
|
127
127
|
@if (showClock()) {
|
|
128
128
|
<div class="flex items-baseline gap-2" aria-live="polite">
|
|
129
129
|
<time
|
|
130
|
-
class="hidden text-caption text-neutral-
|
|
130
|
+
class="hidden text-caption text-neutral-600 md:inline dark:text-neutral-400"
|
|
131
131
|
[dateTime]="nowIso()"
|
|
132
132
|
>
|
|
133
133
|
{{ now() | date: clockDateFormat() }}
|
|
@@ -157,7 +157,7 @@ export class AppShellHeaderEndDirective {}
|
|
|
157
157
|
@if (showNotifications()) {
|
|
158
158
|
<button
|
|
159
159
|
type="button"
|
|
160
|
-
class="relative inline-flex size-9 cursor-pointer items-center justify-center rounded-lg text-neutral-600 transition-colors hover:bg-background-
|
|
160
|
+
class="relative inline-flex size-9 cursor-pointer items-center justify-center rounded-lg text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
|
|
161
161
|
[attr.aria-label]="notificationAriaLabel()"
|
|
162
162
|
(click)="openNotifications()"
|
|
163
163
|
>
|
|
@@ -274,15 +274,15 @@ export class AppShellHeaderComponent {
|
|
|
274
274
|
if (temp === undefined || !Number.isFinite(temp)) {
|
|
275
275
|
return null;
|
|
276
276
|
}
|
|
277
|
-
return
|
|
277
|
+
return String(Math.round(temp));
|
|
278
278
|
});
|
|
279
279
|
|
|
280
|
-
protected readonly
|
|
280
|
+
protected readonly weatherCondition = computed(() => {
|
|
281
281
|
const forecast = this.weather();
|
|
282
282
|
if (!forecast) {
|
|
283
283
|
return '';
|
|
284
284
|
}
|
|
285
|
-
return
|
|
285
|
+
return headerWeatherLabel(forecast.kind, this.now().getHours());
|
|
286
286
|
});
|
|
287
287
|
|
|
288
288
|
protected readonly weatherAriaLabel = computed(() => {
|
|
@@ -290,11 +290,11 @@ export class AppShellHeaderComponent {
|
|
|
290
290
|
if (!forecast) {
|
|
291
291
|
return 'Weather';
|
|
292
292
|
}
|
|
293
|
-
const condition =
|
|
293
|
+
const condition = this.weatherCondition().toLowerCase();
|
|
294
294
|
const temp = this.weatherTemp();
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
295
|
+
return temp === null
|
|
296
|
+
? `Weather: ${condition}`
|
|
297
|
+
: `Weather: ${temp} degrees Celsius, ${condition}`;
|
|
298
298
|
});
|
|
299
299
|
|
|
300
300
|
protected readonly unreadCount = computed(
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
>
|
|
22
22
|
<button
|
|
23
23
|
type="button"
|
|
24
|
-
class="inline-flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-lg border border-
|
|
24
|
+
class="inline-flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-lg border border-control text-ink transition-colors hover:bg-background-hover dark:border-white/15 dark:text-white dark:hover:bg-white/10"
|
|
25
25
|
[attr.aria-label]="mobileNavOpen() ? 'Close navigation' : 'Open navigation'"
|
|
26
26
|
[attr.aria-expanded]="mobileNavOpen()"
|
|
27
27
|
(click)="toggleMobileNav()"
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
<div
|
|
40
|
-
class="sticky top-0 z-30 border-b border-border bg-
|
|
40
|
+
class="sticky top-0 z-30 border-b border-border bg-surface/90 backdrop-blur-md dark:border-white/10 dark:bg-ink-950/90"
|
|
41
41
|
>
|
|
42
42
|
@if (hasCustomHeader()) {
|
|
43
43
|
<ng-content select="[africaniesAppShellHeader]" />
|
|
@@ -229,16 +229,16 @@ export class AppShellComponent {
|
|
|
229
229
|
// Preview embeds: absolute/h-full so the drawer stays inside the frame.
|
|
230
230
|
// Production: h-dvh so the rail fills the viewport (h-full only matches parent).
|
|
231
231
|
if (preview === 'desktop') {
|
|
232
|
-
return 'z-50 shrink-0 self-start sticky top-0 block h-full';
|
|
232
|
+
return 'z-50 shrink-0 self-start sticky top-0 block h-full min-h-0';
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
if (preview === 'mobile' || preview === 'tablet') {
|
|
236
236
|
return open
|
|
237
|
-
? 'z-50 shrink-0 self-start absolute inset-y-0 left-0 h-full shadow-xl'
|
|
237
|
+
? 'z-50 shrink-0 self-start absolute inset-y-0 left-0 h-full min-h-0 shadow-xl'
|
|
238
238
|
: 'hidden';
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
-
const base = 'z-50 shrink-0 self-start h-dvh';
|
|
241
|
+
const base = 'z-50 shrink-0 self-start h-dvh min-h-0';
|
|
242
242
|
if (open) {
|
|
243
243
|
return `${base} fixed inset-y-0 left-0 shadow-xl lg:sticky lg:top-0 lg:block`;
|
|
244
244
|
}
|
|
@@ -33,8 +33,6 @@ export interface HeaderWeather {
|
|
|
33
33
|
kind: HeaderWeatherKind;
|
|
34
34
|
/** Celsius, when the forecast includes it. */
|
|
35
35
|
temperatureC?: number;
|
|
36
|
-
/** City from IP geolocation, when known. */
|
|
37
|
-
city?: string;
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
const PERIOD_KICKERS: Record<HeaderGreetingPeriod, readonly string[]> = {
|
|
@@ -66,7 +66,7 @@ describe('loadHeaderWeather', () => {
|
|
|
66
66
|
).resolves.toBeNull();
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
-
it('reads Open-Meteo after IP
|
|
69
|
+
it('reads Open-Meteo after IP coordinates', async () => {
|
|
70
70
|
const fetchFn = jest.fn(async (url: string) => {
|
|
71
71
|
if (String(url).includes('geojs')) {
|
|
72
72
|
return jsonResponse({
|
|
@@ -75,9 +75,6 @@ describe('loadHeaderWeather', () => {
|
|
|
75
75
|
city: 'Lagos',
|
|
76
76
|
});
|
|
77
77
|
}
|
|
78
|
-
if (String(url).includes('bigdatacloud')) {
|
|
79
|
-
return jsonResponse({ city: 'Abuja', locality: 'Abuja' });
|
|
80
|
-
}
|
|
81
78
|
return jsonResponse({
|
|
82
79
|
current: { weather_code: 61, temperature_2m: 27.4 },
|
|
83
80
|
});
|
|
@@ -88,29 +85,8 @@ describe('loadHeaderWeather', () => {
|
|
|
88
85
|
).resolves.toEqual({
|
|
89
86
|
kind: 'rain',
|
|
90
87
|
temperatureC: 27.4,
|
|
91
|
-
city: 'Abuja',
|
|
92
|
-
});
|
|
93
|
-
expect(fetchFn).toHaveBeenCalledTimes(3);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('falls back to the IP city when reverse geocode is empty', async () => {
|
|
97
|
-
const fetchFn = jest.fn(async (url: string) => {
|
|
98
|
-
if (String(url).includes('geojs')) {
|
|
99
|
-
return jsonResponse({ latitude: 1, longitude: 2, city: 'Lagos' });
|
|
100
|
-
}
|
|
101
|
-
if (String(url).includes('bigdatacloud')) {
|
|
102
|
-
return jsonResponse({});
|
|
103
|
-
}
|
|
104
|
-
return jsonResponse({ current: { weather_code: 0, temperature_2m: 30 } });
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
await expect(
|
|
108
|
-
loadHeaderWeather(fetchFn as unknown as typeof fetch),
|
|
109
|
-
).resolves.toEqual({
|
|
110
|
-
kind: 'clear',
|
|
111
|
-
temperatureC: 30,
|
|
112
|
-
city: 'Lagos',
|
|
113
88
|
});
|
|
89
|
+
expect(fetchFn).toHaveBeenCalledTimes(2);
|
|
114
90
|
});
|
|
115
91
|
|
|
116
92
|
it('reuses the same-hour session cache', async () => {
|
|
@@ -118,9 +94,6 @@ describe('loadHeaderWeather', () => {
|
|
|
118
94
|
if (String(url).includes('geojs')) {
|
|
119
95
|
return jsonResponse({ latitude: 1, longitude: 2 });
|
|
120
96
|
}
|
|
121
|
-
if (String(url).includes('bigdatacloud')) {
|
|
122
|
-
return jsonResponse({ city: 'Accra' });
|
|
123
|
-
}
|
|
124
97
|
return jsonResponse({ current: { weather_code: 0, temperature_2m: 30 } });
|
|
125
98
|
});
|
|
126
99
|
|
|
@@ -128,16 +101,13 @@ describe('loadHeaderWeather', () => {
|
|
|
128
101
|
const second = await loadHeaderWeather(fetchFn as unknown as typeof fetch);
|
|
129
102
|
|
|
130
103
|
expect(first).toEqual(second);
|
|
131
|
-
expect(fetchFn).toHaveBeenCalledTimes(
|
|
104
|
+
expect(fetchFn).toHaveBeenCalledTimes(2);
|
|
132
105
|
});
|
|
133
106
|
|
|
134
107
|
it('fetches again when the cached hour no longer matches', async () => {
|
|
135
108
|
const fetchFn = jest.fn(async (url: string) => {
|
|
136
109
|
if (String(url).includes('geojs')) {
|
|
137
|
-
return jsonResponse({ latitude: 1, longitude: 2
|
|
138
|
-
}
|
|
139
|
-
if (String(url).includes('bigdatacloud')) {
|
|
140
|
-
return jsonResponse({ city: 'Lagos' });
|
|
110
|
+
return jsonResponse({ latitude: 1, longitude: 2 });
|
|
141
111
|
}
|
|
142
112
|
return jsonResponse({ current: { weather_code: 0, temperature_2m: 30.2 } });
|
|
143
113
|
});
|
|
@@ -154,7 +124,7 @@ describe('loadHeaderWeather', () => {
|
|
|
154
124
|
sessionStorage.setItem(cacheKey!, JSON.stringify(stored));
|
|
155
125
|
|
|
156
126
|
await loadHeaderWeather(fetchFn as unknown as typeof fetch);
|
|
157
|
-
expect(fetchFn).toHaveBeenCalledTimes(
|
|
127
|
+
expect(fetchFn).toHaveBeenCalledTimes(4);
|
|
158
128
|
});
|
|
159
129
|
|
|
160
130
|
it('uses browser coordinates when geolocation succeeds', async () => {
|
|
@@ -181,9 +151,6 @@ describe('loadHeaderWeather', () => {
|
|
|
181
151
|
});
|
|
182
152
|
|
|
183
153
|
const fetchFn = jest.fn(async (url: string) => {
|
|
184
|
-
if (String(url).includes('bigdatacloud')) {
|
|
185
|
-
return jsonResponse({ city: 'Abuja' });
|
|
186
|
-
}
|
|
187
154
|
if (String(url).includes('geojs')) {
|
|
188
155
|
throw new Error('should not call IP geo');
|
|
189
156
|
}
|
|
@@ -195,7 +162,6 @@ describe('loadHeaderWeather', () => {
|
|
|
195
162
|
).resolves.toEqual({
|
|
196
163
|
kind: 'clear',
|
|
197
164
|
temperatureC: 31,
|
|
198
|
-
city: 'Abuja',
|
|
199
165
|
});
|
|
200
166
|
expect(
|
|
201
167
|
fetchFn.mock.calls.some((call) => String(call[0]).includes('geojs')),
|
|
@@ -206,7 +172,12 @@ describe('loadHeaderWeather', () => {
|
|
|
206
172
|
describe('headerWeatherLabel', () => {
|
|
207
173
|
it('returns a short condition', () => {
|
|
208
174
|
expect(headerWeatherLabel('rain')).toBe('Rain');
|
|
209
|
-
expect(headerWeatherLabel('
|
|
175
|
+
expect(headerWeatherLabel('cloudy')).toBe('Cloudy');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('uses Sunny by day and Clear at night', () => {
|
|
179
|
+
expect(headerWeatherLabel('clear', 10)).toBe('Sunny');
|
|
180
|
+
expect(headerWeatherLabel('clear', 22)).toBe('Clear');
|
|
210
181
|
});
|
|
211
182
|
});
|
|
212
183
|
|