@abgov/nx-adsp 13.18.0 → 13.18.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/package.json
CHANGED
|
@@ -236,31 +236,22 @@ mark what's meant to be edited.
|
|
|
236
236
|
```
|
|
237
237
|
3. **For the `internal` layout: add a nav link to `src/App.vue`.** The generated shell only
|
|
238
238
|
populates `accountItems`; primary navigation lives in `primaryItems` (or `secondaryItems`).
|
|
239
|
-
`AppSideMenu`
|
|
240
|
-
`
|
|
239
|
+
`AppSideMenu` emits one `itemClick` for all slots — the generated `onItemClick` already
|
|
240
|
+
dispatches by `item.to`: items with a `to` route, items without sign in/out. Add only
|
|
241
|
+
`primaryItems`:
|
|
241
242
|
|
|
242
243
|
```typescript
|
|
243
|
-
// In <script setup
|
|
244
|
-
|
|
245
|
-
import { RouterView, useRoute, useRouter } from 'vue-router';
|
|
246
|
-
// ...existing imports...
|
|
247
|
-
|
|
248
|
-
const route = useRoute(); // already in the generated App.vue
|
|
249
|
-
const router = useRouter();
|
|
250
|
-
|
|
251
|
-
// Icon names come from the GoA icon set: design.alberta.ca/components/icons
|
|
244
|
+
// In <script setup> — route, router, and onItemClick are already in the generated App.vue:
|
|
245
|
+
// Icon names from the GoA icon set: design.alberta.ca/components/icons
|
|
252
246
|
const primaryItems = computed(() => [
|
|
253
|
-
{ label: 'Home', to: '/', icon: 'home',
|
|
254
|
-
{ label: 'My Feature', to: '/my-feature', icon: 'list',
|
|
255
|
-
])
|
|
256
|
-
|
|
257
|
-
function onItemClick(item: { to?: string }) {
|
|
258
|
-
if (item.to) router.push(item.to);
|
|
259
|
-
}
|
|
247
|
+
{ label: 'Home', to: '/', icon: 'home', current: route.path === '/' },
|
|
248
|
+
{ label: 'My Feature', to: '/my-feature', icon: 'list', current: route.path.startsWith('/my-feature') },
|
|
249
|
+
])
|
|
260
250
|
```
|
|
261
251
|
|
|
262
252
|
```html
|
|
263
|
-
<!-- In <template>, add :primary-items
|
|
253
|
+
<!-- In <template>, add :primary-items to <AppSideMenu>
|
|
254
|
+
(@item-click="onItemClick" is already wired in the generated shell): -->
|
|
264
255
|
<AppSideMenu
|
|
265
256
|
heading="..."
|
|
266
257
|
:primary-items="primaryItems"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from 'vue';
|
|
3
3
|
import { useKeycloak } from '@dsb-norge/vue-keycloak-js';
|
|
4
|
-
import { RouterView, useRoute } from 'vue-router';
|
|
4
|
+
import { RouterView, useRoute<% if (layout === 'internal') { %>, useRouter<% } %> } from 'vue-router';
|
|
5
5
|
<% if (layout === 'internal') { %>
|
|
6
6
|
import { AppLayout, AppSideMenu, SessionExpiredBanner } from '<%= goaImportPath %>';
|
|
7
7
|
<% } else { %>
|
|
@@ -32,16 +32,21 @@ function signInAgain() {
|
|
|
32
32
|
}
|
|
33
33
|
<% if (layout === 'internal') { %>
|
|
34
34
|
|
|
35
|
-
// AppSideMenu's
|
|
36
|
-
//
|
|
35
|
+
// AppSideMenu's items are presentational — it doesn't know about Keycloak or
|
|
36
|
+
// routing; it just renders whatever's passed and emits itemClick on click.
|
|
37
37
|
const accountItems = computed(() => [
|
|
38
38
|
kc.authenticated
|
|
39
39
|
? { label: kc.fullName ? `Sign out (${kc.fullName})` : 'Sign out' }
|
|
40
40
|
: { label: 'Sign in' },
|
|
41
41
|
]);
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
const router = useRouter();
|
|
44
|
+
|
|
45
|
+
// AppSideMenu emits one itemClick for ALL slots (primary, secondary, account).
|
|
46
|
+
// Items with `to` are nav items — route. Items without are account actions — sign in/out.
|
|
47
|
+
function onItemClick(item: { to?: string }) {
|
|
48
|
+
if (item.to) router.push(item.to);
|
|
49
|
+
else if (kc.authenticated) logout();
|
|
45
50
|
else login();
|
|
46
51
|
}
|
|
47
52
|
<% } %>
|
|
@@ -52,7 +57,7 @@ function onAccountItemClick() {
|
|
|
52
57
|
<AppSideMenu
|
|
53
58
|
heading="<%= projectName %>"
|
|
54
59
|
:account-items="accountItems"
|
|
55
|
-
@item-click="
|
|
60
|
+
@item-click="onItemClick"
|
|
56
61
|
>
|
|
57
62
|
<SessionExpiredBanner
|
|
58
63
|
v-model:show="session.expired"
|
|
@@ -3,9 +3,9 @@ import { ref, onMounted, watch } from 'vue'
|
|
|
3
3
|
import { useKeycloak } from '@dsb-norge/vue-keycloak-js'
|
|
4
4
|
import { useApi } from '../composables/useApi'
|
|
5
5
|
|
|
6
|
-
// Keep the
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// Keep the useKeycloak() result as an object — do NOT destructure it (see App.vue):
|
|
7
|
+
// its fields are plain values in a reactive object and freeze at setup-time undefined
|
|
8
|
+
// when extracted. `apiFetch` from useApi() is a plain function and is fine to destructure.
|
|
9
9
|
const kc = useKeycloak()
|
|
10
10
|
const { apiFetch } = useApi()
|
|
11
11
|
const publicResource = ref('Not retrieved — is the backend service running?')
|
|
@@ -206,7 +206,7 @@ describe('Vue App Generator', () => {
|
|
|
206
206
|
expect(app).toContain('<AppSideMenu');
|
|
207
207
|
expect(app).toContain('heading="test"');
|
|
208
208
|
expect(app).toContain(':account-items="accountItems"');
|
|
209
|
-
expect(app).toContain('@item-click="
|
|
209
|
+
expect(app).toContain('@item-click="onItemClick"');
|
|
210
210
|
// The content gutter is shared regardless of shell choice.
|
|
211
211
|
expect(app).toContain('<AppLayout');
|
|
212
212
|
// App.vue itself must not add a second skip-to-main-content landmark —
|
|
@@ -353,6 +353,21 @@ describe('Vue App Generator', () => {
|
|
|
353
353
|
expect(homeView).toContain('goa-hero-banner');
|
|
354
354
|
}, 30000);
|
|
355
355
|
|
|
356
|
+
it('--layout=internal App.vue uses a unified onItemClick that routes nav items and signs in/out for account items', async () => {
|
|
357
|
+
// AppSideMenu emits one itemClick for all slots. A dedicated onAccountItemClick breaks
|
|
358
|
+
// once primaryItems are added — primary nav clicks fire login()/logout() instead of routing.
|
|
359
|
+
// The generated handler must dispatch by item.to.
|
|
360
|
+
await generator(host, { ...options, layout: 'internal' });
|
|
361
|
+
const app = host.read('apps/test/src/App.vue').toString();
|
|
362
|
+
expect(app).toContain('onItemClick');
|
|
363
|
+
expect(app).toContain('router.push');
|
|
364
|
+
expect(app).not.toContain('onAccountItemClick');
|
|
365
|
+
// Routing branch: items with `to` navigate
|
|
366
|
+
expect(app).toContain('item.to');
|
|
367
|
+
// Auth branch: items without `to` fall through to sign-in/out
|
|
368
|
+
expect(app).toContain('kc.authenticated');
|
|
369
|
+
}, 30000);
|
|
370
|
+
|
|
356
371
|
it('--layout=internal has no hero banner anywhere, including on HomeView', async () => {
|
|
357
372
|
await generator(host, { ...options, layout: 'internal' });
|
|
358
373
|
const homeView = host.read('apps/test/src/views/HomeView.vue').toString();
|