@abgov/nx-adsp 13.17.1 → 13.18.0-beta.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.
Files changed (24) hide show
  1. package/package.json +1 -1
  2. package/src/generators/vue-app/files/AGENTS.md__tmpl__ +25 -7
  3. package/src/generators/vue-app/files/src/App.spec.ts__tmpl__ +44 -2
  4. package/src/generators/vue-app/files/src/App.vue__tmpl__ +18 -10
  5. package/src/generators/vue-app/files/src/main.ts__tmpl__ +10 -1
  6. package/src/generators/vue-app/files/src/stores/session.ts__tmpl__ +16 -0
  7. package/src/generators/vue-app/vue-app.js +3 -0
  8. package/src/generators/vue-app/vue-app.js.map +1 -1
  9. package/src/generators/vue-app/vue-app.spec.ts +68 -11
  10. package/src/generators/vue-components/files/AGENTS.md__tmpl__ +44 -14
  11. package/src/generators/vue-components/files/src/index.ts__tmpl__ +23 -12
  12. package/src/generators/vue-components/files/src/lib/patterns/AppFooter.vue__tmpl__ +14 -0
  13. package/src/generators/vue-components/files/src/lib/patterns/AppHeader.vue__tmpl__ +19 -0
  14. package/src/generators/{vue-app/files/src/components → vue-components/files/src/lib/patterns}/AppLayout.vue__tmpl__ +17 -0
  15. package/src/generators/vue-components/files/src/lib/patterns/SessionExpiredBanner.vue__tmpl__ +28 -0
  16. package/src/generators/vue-components/files/src/vue-components.spec.ts__tmpl__ +15 -4
  17. package/src/generators/vue-components/vue-components.spec.ts +14 -3
  18. /package/src/generators/vue-components/files/src/lib/{GoabButton.vue__tmpl__ → primitives/GoabButton.vue__tmpl__} +0 -0
  19. /package/src/generators/vue-components/files/src/lib/{GoabCheckbox.vue__tmpl__ → primitives/GoabCheckbox.vue__tmpl__} +0 -0
  20. /package/src/generators/vue-components/files/src/lib/{GoabDropdown.vue__tmpl__ → primitives/GoabDropdown.vue__tmpl__} +0 -0
  21. /package/src/generators/vue-components/files/src/lib/{GoabInput.vue__tmpl__ → primitives/GoabInput.vue__tmpl__} +0 -0
  22. /package/src/generators/vue-components/files/src/lib/{GoabModal.vue__tmpl__ → primitives/GoabModal.vue__tmpl__} +0 -0
  23. /package/src/generators/vue-components/files/src/lib/{GoabRadioGroup.vue__tmpl__ → primitives/GoabRadioGroup.vue__tmpl__} +0 -0
  24. /package/src/generators/vue-components/files/src/lib/{GoabTextarea.vue__tmpl__ → primitives/GoabTextarea.vue__tmpl__} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abgov/nx-adsp",
3
- "version": "13.17.1",
3
+ "version": "13.18.0-beta.2",
4
4
  "license": "Apache-2.0",
5
5
  "main": "src/index.js",
6
6
  "description": "Government of Alberta - Nx plugin for ADSP apps.",
@@ -30,9 +30,9 @@ executors read options from `project.json` and do not prompt.
30
30
 
31
31
  | File | Purpose |
32
32
  |------|---------|
33
- | `src/main.ts` | Entry — registers Pinia, Router, and Keycloak plugin |
34
- | `src/App.vue` | Shell — nav header with sign-in/out, hero banner, `<RouterView>` wrapped in `AppLayout` |
35
- | `src/components/AppLayout.vue` | Shared content gutter (centered, max-width, token padding). Every view lands inside it — no per-view spacing to add. Width via route meta `layout` |
33
+ | `src/main.ts` | Entry — registers Pinia, Router, and Keycloak plugin (incl. `onAuthRefreshError`, which flags the session store as expired) |
34
+ | `src/App.vue` | Shell — `AppHeader` with sign-in/out in its `utilities` slot, `SessionExpiredBanner`, hero banner, `<RouterView>` wrapped in `AppLayout`, `AppFooter` |
35
+ | `src/stores/session.ts` | Pinia store `expired` flag behind `SessionExpiredBanner`, set from `main.ts`'s `onAuthRefreshError` hook |
36
36
  | `src/router/index.ts` | Routes — `/protected` guarded with `requiresAuth` meta |
37
37
  | `src/views/HomeView.vue` | Public page — calls public and private APIs |
38
38
  | `src/views/ProtectedView.vue` | Authenticated page — shows user info from token |
@@ -151,10 +151,28 @@ the design system supports still works. For components without a wrapper, use th
151
151
  across every Vue app in this workspace — fix or extend a wrapper once in
152
152
  `libs/vue-components` (or run `nx g @abgov/nx-adsp:vue-components` to restore it).
153
153
 
154
- > **Interim:** these wrappers are a stopgap until GoA DS ships an official
155
- > `@abgov/vue-components`. When it lands, delete the `vue-components` lib and
156
- > repoint imports at that package — the names/props are intended to match, so
157
- > it's a scope swap.
154
+ > **Interim:** these wrappers (`libs/vue-components/src/lib/primitives/`) are a
155
+ > stopgap until GoA DS ships an official `@abgov/vue-components`. When it lands,
156
+ > delete `primitives/` and repoint imports at that package — the names/props are
157
+ > intended to match, so it's a scope swap. This does **not** apply to the
158
+ > `AppHeader`/`AppLayout`/`AppFooter`/`SessionExpiredBanner` shell components
159
+ > below — those live in the same library's `patterns/` folder and are permanent
160
+ > (app-shell composition has no equivalent in a design-system package).
161
+
162
+ ### App shell components
163
+
164
+ `App.vue` is built from four shared components imported from
165
+ **`<%= goaImportPath %>`**, not hand-rolled markup:
166
+
167
+ | Component | Purpose |
168
+ |---|---|
169
+ | `AppHeader` | `goa-microsite-header` + `goa-app-header`. Takes a `heading` prop; put account/sign-in actions in its `utilities` slot (a bare child with no slot is silently dropped by `goa-app-header`) |
170
+ | `AppLayout` | The content gutter every view renders inside (see the key files table). Also renders the skip-to-main-content link |
171
+ | `AppFooter` | `goa-app-footer` with the standard GoA nav/meta links (Services/Contact/Terms of Use, Privacy/Disclaimer/Accessibility) |
172
+ | `SessionExpiredBanner` | A `v-model:show` banner with `signIn`/`dismiss` emits. Bound to the `useSessionStore()` Pinia store (`src/stores/session.ts`), which `main.ts`'s `onAuthRefreshError` hook flips when the refresh token itself has expired |
173
+
174
+ These are shared across every Vue app in this workspace — fix or extend one once
175
+ in `libs/vue-components/src/lib/patterns/`, the same way as the `Goab*` wrappers.
158
176
 
159
177
  ## Adding a view
160
178
 
@@ -16,8 +16,10 @@ vi.mock('@dsb-norge/vue-keycloak-js', async () => {
16
16
  });
17
17
 
18
18
  import { useKeycloak } from '@dsb-norge/vue-keycloak-js';
19
+ import { createPinia } from 'pinia';
19
20
  import { createMemoryHistory, createRouter } from 'vue-router';
20
21
  import App from './App.vue';
22
+ import { useSessionStore } from './stores/session';
21
23
 
22
24
  // App.vue reads useRoute() for the layout-width feature (route.meta.layout,
23
25
  // default 'page') — that needs a real router installed via `global.plugins`,
@@ -35,7 +37,9 @@ const router = createRouter({
35
37
 
36
38
  describe('App shell header', () => {
37
39
  it('shows Sign in when unauthenticated', () => {
38
- const wrapper = mount(App, { global: { plugins: [router], stubs: { RouterView: true } } });
40
+ const wrapper = mount(App, {
41
+ global: { plugins: [router, createPinia()], stubs: { RouterView: true } },
42
+ });
39
43
  const group = wrapper.find('goa-button-group');
40
44
  expect(group.exists()).toBe(true);
41
45
  expect(group.html()).toContain('Sign in');
@@ -45,9 +49,47 @@ describe('App shell header', () => {
45
49
  // The bug: destructuring useKeycloak() froze `keycloak` at undefined, so
46
50
  // login() was a permanent no-op. Reactive access must reach the instance.
47
51
  const kc = useKeycloak();
48
- const wrapper = mount(App, { global: { plugins: [router], stubs: { RouterView: true } } });
52
+ const wrapper = mount(App, {
53
+ global: { plugins: [router, createPinia()], stubs: { RouterView: true } },
54
+ });
49
55
  const btn = wrapper.findAll('goa-button').find((b) => b.text().includes('Sign in'));
50
56
  await btn?.trigger('_click');
51
57
  expect(kc.keycloak?.login).toHaveBeenCalled();
52
58
  });
53
59
  });
60
+
61
+ describe('App shell session-expired banner', () => {
62
+ it('is hidden until the session store flags an expired session', async () => {
63
+ const pinia = createPinia();
64
+ const wrapper = mount(App, {
65
+ global: { plugins: [router, pinia], stubs: { RouterView: true } },
66
+ });
67
+ expect(wrapper.find('.session-expired-banner').exists()).toBe(false);
68
+
69
+ useSessionStore(pinia).markExpired();
70
+ await wrapper.vm.$nextTick();
71
+ expect(wrapper.find('.session-expired-banner').exists()).toBe(true);
72
+ });
73
+
74
+ it('Sign in on the banner dismisses it and calls keycloak.login', async () => {
75
+ const kc = useKeycloak();
76
+ const pinia = createPinia();
77
+ const wrapper = mount(App, {
78
+ global: { plugins: [router, pinia], stubs: { RouterView: true } },
79
+ });
80
+ useSessionStore(pinia).markExpired();
81
+ await wrapper.vm.$nextTick();
82
+
83
+ // SessionExpiredBanner's "Sign in" is the GoabButton wrapper, which re-exposes
84
+ // goa-button's `_click` as a plain `click` on itself -- but the underlying DOM
85
+ // node vue-test-utils sees is still the raw <goa-button>, so trigger `_click`
86
+ // (same as AppHeader's raw Sign in button above), not `click`.
87
+ const btn = wrapper
88
+ .findAll('goa-button')
89
+ .find((b) => b.text().includes('Sign in') && b.attributes('type') === 'primary');
90
+ await btn?.trigger('_click');
91
+
92
+ expect(kc.keycloak?.login).toHaveBeenCalled();
93
+ expect(useSessionStore(pinia).expired).toBe(false);
94
+ });
95
+ });
@@ -2,13 +2,15 @@
2
2
  import { computed } from 'vue';
3
3
  import { useKeycloak } from '@dsb-norge/vue-keycloak-js';
4
4
  import { RouterView, useRoute } from 'vue-router';
5
- import AppLayout from './components/AppLayout.vue';
5
+ import { AppHeader, AppLayout, AppFooter, SessionExpiredBanner } from '<%= goaImportPath %>';
6
+ import { useSessionStore } from './stores/session';
6
7
 
7
8
  // Keep the reactive instance — do NOT destructure. useKeycloak() returns a
8
9
  // readonly(reactive()); destructuring snapshots each field at setup time, so
9
10
  // `keycloak` would be frozen at its initial `undefined` (login() a no-op) and
10
11
  // `authenticated` would never flip. Access fields on `kc` to stay reactive.
11
12
  const kc = useKeycloak();
13
+ const session = useSessionStore();
12
14
 
13
15
  // Content width per view, from route meta (default 'page'). Views opt into a
14
16
  // different width with `meta: { layout: 'wide' | 'form' }` in the router.
@@ -19,16 +21,16 @@ const layout = computed(
19
21
 
20
22
  function login() { kc.keycloak?.login(); }
21
23
  function logout() { kc.keycloak?.logout({ redirectUri: window.location.origin }); }
24
+
25
+ function signInAgain() {
26
+ session.dismiss();
27
+ kc.keycloak?.login();
28
+ }
22
29
  </script>
23
30
 
24
31
  <template>
25
- <goa-microsite-header type="alpha" />
26
- <goa-app-header url="/" heading="<%= projectName %>">
27
- <!-- goa-app-header only renders content placed in a named slot — the
28
- "utilities" slot is where GoA's own design system docs put
29
- account/sign-in actions (as opposed to "navigation" for nav links).
30
- A bare child with no slot attribute renders nothing. -->
31
- <div slot="utilities">
32
+ <AppHeader heading="<%= projectName %>">
33
+ <template #utilities>
32
34
  <goa-button-group alignment="end">
33
35
  <span v-if="kc.authenticated && kc.fullName">{{ kc.fullName }}</span>
34
36
  <!-- Gate on !authenticated only, never on `ready`: with no load-time SSO
@@ -41,12 +43,18 @@ function logout() { kc.keycloak?.logout({ redirectUri: window.location.origin })
41
43
  Sign out
42
44
  </goa-button>
43
45
  </goa-button-group>
44
- </div>
45
- </goa-app-header>
46
+ </template>
47
+ </AppHeader>
48
+ <SessionExpiredBanner
49
+ v-model:show="session.expired"
50
+ @sign-in="signInAgain"
51
+ @dismiss="session.dismiss"
52
+ />
46
53
  <goa-hero-banner heading="<%= projectName %>" backgroundurl="/assets/banner.jpg" />
47
54
  <AppLayout :variant="layout">
48
55
  <RouterView />
49
56
  </AppLayout>
57
+ <AppFooter />
50
58
  </template>
51
59
 
52
60
  <style>
@@ -12,10 +12,12 @@ import keycloakPlugin from '@dsb-norge/vue-keycloak-js';
12
12
  import App from './App.vue';
13
13
  import router from './router';
14
14
  import { environment } from './environments/environment';
15
+ import { useSessionStore } from './stores/session';
15
16
 
16
17
  const app = createApp(App);
18
+ const pinia = createPinia();
17
19
 
18
- app.use(createPinia());
20
+ app.use(pinia);
19
21
  app.use(router);
20
22
  app.use(keycloakPlugin, {
21
23
  config: {
@@ -41,6 +43,13 @@ app.use(keycloakPlugin, {
41
43
  pkceMethod: 'S256',
42
44
  checkLoginIframe: false,
43
45
  },
46
+ // Not onAuthLogout: keycloak-js only invokes that via the session-status
47
+ // iframe (disabled above by checkLoginIframe: false) or in Cordova mode --
48
+ // it would never fire here. The wrapper's own background auto-refresh
49
+ // (autoRefreshToken, on by default) periodically calls updateToken();
50
+ // onAuthRefreshError fires when that fails because the refresh token itself
51
+ // has expired, which is the real, always-on "session expired" signal.
52
+ onAuthRefreshError: () => useSessionStore(pinia).markExpired(),
44
53
  });
45
54
 
46
55
  app.mount('#app');
@@ -0,0 +1,16 @@
1
+ import { defineStore } from 'pinia';
2
+
3
+ // Tracks whether the user's session has expired, surfaced via
4
+ // SessionExpiredBanner in App.vue. Set from main.ts's onAuthRefreshError hook
5
+ // -- see that file for why that hook, not onAuthLogout, is the real signal.
6
+ export const useSessionStore = defineStore('session', {
7
+ state: () => ({ expired: false }),
8
+ actions: {
9
+ markExpired() {
10
+ this.expired = true;
11
+ },
12
+ dismiss() {
13
+ this.expired = false;
14
+ },
15
+ },
16
+ });
@@ -122,6 +122,9 @@ function default_1(host, options) {
122
122
  'src/app/App.spec.ts',
123
123
  'src/app/NxWelcome.vue',
124
124
  'src/components/HelloWorld.vue',
125
+ // AppLayout now lives in the shared vue-components lib (patterns/) —
126
+ // remove a stale copy from an app scaffolded before that move.
127
+ 'src/components/AppLayout.vue',
125
128
  'src/views/AboutView.vue',
126
129
  'vite.config.mts',
127
130
  ]) {
@@ -1 +1 @@
1
- {"version":3,"file":"vue-app.js","sourceRoot":"","sources":["../../../../../../packages/nx-adsp/src/generators/vue-app/vue-app.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"vue-app.js","sourceRoot":"","sources":["../../../../../../packages/nx-adsp/src/generators/vue-app/vue-app.ts"],"names":[],"mappings":"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"}
@@ -116,15 +116,16 @@ describe('Vue App Generator', () => {
116
116
  it('wraps views in a shared AppLayout gutter (not a bare tag selector)', async () => {
117
117
  await generator(host, options);
118
118
 
119
- // Shared layout component provides the content gutter for every view.
120
- expect(host.exists('apps/test/src/components/AppLayout.vue')).toBeTruthy();
121
- const layout = host
122
- .read('apps/test/src/components/AppLayout.vue')
123
- .toString();
119
+ // Layout is a pattern component in the shared lib, not copied into the app.
120
+ const layoutPath = 'libs/vue-components/src/lib/patterns/AppLayout.vue';
121
+ expect(host.exists(layoutPath)).toBeTruthy();
122
+ const layout = host.read(layoutPath).toString();
124
123
  // Three named width variants, token-driven padding.
125
124
  expect(layout).toContain('form-content');
126
125
  expect(layout).toContain('wide-content');
127
126
  expect(layout).toContain('--goa-space');
127
+ // Skip-to-main-content link for keyboard/screen-reader users.
128
+ expect(layout).toContain('href="#main-content"');
128
129
 
129
130
  // App.vue uses AppLayout and no longer relies on the `main > section` gutter
130
131
  // (which silently failed when a view's top-level tag wasn't <section>).
@@ -137,7 +138,7 @@ describe('Vue App Generator', () => {
137
138
  await generator(host, options);
138
139
 
139
140
  // Wrappers live in a shared workspace lib, not copied into the app.
140
- const base = 'libs/vue-components/src/lib';
141
+ const primitives = 'libs/vue-components/src/lib/primitives';
141
142
  for (const name of [
142
143
  'GoabInput',
143
144
  'GoabTextarea',
@@ -147,12 +148,12 @@ describe('Vue App Generator', () => {
147
148
  'GoabButton',
148
149
  'GoabModal',
149
150
  ]) {
150
- expect(host.exists(`${base}/${name}.vue`)).toBeTruthy();
151
+ expect(host.exists(`${primitives}/${name}.vue`)).toBeTruthy();
151
152
  }
152
153
  expect(host.exists('apps/test/src/components/goa')).toBeFalsy();
153
154
 
154
155
  // Real v-model wiring: bind :value and read the new value off the GoA event.
155
- const input = host.read(`${base}/GoabInput.vue`).toString();
156
+ const input = host.read(`${primitives}/GoabInput.vue`).toString();
156
157
  expect(input).toContain('defineModel');
157
158
  expect(input).toContain('.detail.value');
158
159
 
@@ -163,6 +164,55 @@ describe('Vue App Generator', () => {
163
164
  expect(agents).toContain('/vue-components');
164
165
  }, 30000);
165
166
 
167
+ it('provisions the shared app-shell pattern components and App.vue imports them', async () => {
168
+ await generator(host, options);
169
+
170
+ const patterns = 'libs/vue-components/src/lib/patterns';
171
+ for (const name of [
172
+ 'AppLayout',
173
+ 'AppHeader',
174
+ 'AppFooter',
175
+ 'SessionExpiredBanner',
176
+ ]) {
177
+ expect(host.exists(`${patterns}/${name}.vue`)).toBeTruthy();
178
+ }
179
+
180
+ const app = host.read('apps/test/src/App.vue').toString();
181
+ expect(app).toContain(
182
+ "import { AppHeader, AppLayout, AppFooter, SessionExpiredBanner } from '@proj/vue-components';",
183
+ );
184
+ expect(app).toContain('<AppHeader heading="test">');
185
+ expect(app).toContain('<AppFooter />');
186
+ }, 30000);
187
+
188
+ it('wires SessionExpiredBanner to a session store fed by onAuthRefreshError (not onAuthLogout)', async () => {
189
+ await generator(host, options);
190
+
191
+ expect(host.exists('apps/test/src/stores/session.ts')).toBeTruthy();
192
+ const store = host.read('apps/test/src/stores/session.ts').toString();
193
+ expect(store).toContain("defineStore('session'");
194
+ expect(store).toContain('markExpired');
195
+
196
+ // Strip // comments — main.ts legitimately explains, in prose, why
197
+ // onAuthLogout is the wrong hook; assert against the actual init code.
198
+ const mainTsCode = host
199
+ .read('apps/test/src/main.ts')
200
+ .toString()
201
+ .split('\n')
202
+ .filter((l) => !l.trim().startsWith('//'))
203
+ .join('\n');
204
+ expect(mainTsCode).toContain('onAuthRefreshError');
205
+ // Regression guard: onAuthLogout only fires via the session-status iframe
206
+ // (disabled here) or Cordova mode — it would never fire in this app.
207
+ expect(mainTsCode).not.toContain('onAuthLogout');
208
+ expect(mainTsCode).toContain('useSessionStore(pinia).markExpired()');
209
+
210
+ const app = host.read('apps/test/src/App.vue').toString();
211
+ expect(app).toContain('v-model:show="session.expired"');
212
+ expect(app).toContain('@sign-in="signInAgain"');
213
+ expect(app).toContain('@dismiss="session.dismiss"');
214
+ }, 30000);
215
+
166
216
  it('inits Keycloak with no hidden iframes so init never hangs', async () => {
167
217
  await generator(host, options);
168
218
  // Strip // comments — they legitimately reference the disabled iframe options
@@ -255,11 +305,18 @@ describe('Vue App Generator', () => {
255
305
  // goa-app-header (v2) only renders content placed in a named slot — GoA's
256
306
  // own design system docs put account/sign-in actions in "utilities" (vs.
257
307
  // "navigation" for nav links). A bare child with no slot attribute
258
- // renders nothing, silently dropping the sign-in button. Regression
259
- // guard for exactly that gap.
308
+ // renders nothing, silently dropping the sign-in button. AppHeader (the
309
+ // shared pattern component) owns the native `slot="utilities"` div; App.vue
310
+ // just feeds its named Vue slot. Regression guard for exactly that gap,
311
+ // now spanning both files since AppHeader was extracted out of App.vue.
260
312
  await generator(host, options);
313
+ const appHeader = host
314
+ .read('libs/vue-components/src/lib/patterns/AppHeader.vue')
315
+ .toString();
316
+ expect(appHeader).toMatch(/<div[^>]*slot="utilities"[^>]*>[\s\S]*<slot name="utilities"/);
317
+
261
318
  const app = host.read('apps/test/src/App.vue').toString();
262
- expect(app).toMatch(/<div slot="utilities">[\s\S]*<goa-button-group/);
319
+ expect(app).toMatch(/<template #utilities>[\s\S]*<goa-button-group/);
263
320
  }, 30000);
264
321
 
265
322
  it('removes the @nx/vue demo scaffold and ships a passing App test', async () => {
@@ -1,16 +1,25 @@
1
- # AGENTS.md — vue-components (GoA Design System Vue wrappers)
1
+ # AGENTS.md — vue-components (GoA Design System Vue wrappers + app shell)
2
2
 
3
- A shared, workspace-local library of thin Vue wrappers over the GoA Design System
4
- `goa-*` web components (`@abgov/web-components`). Every Vue app in this workspace
5
- imports these instead of carrying its own copy. Generated by
3
+ A shared, workspace-local library with two distinct kinds of content every Vue
4
+ app in this workspace imports both instead of carrying its own copy. Generated by
6
5
  `@abgov/nx-adsp:vue-components`.
7
6
 
8
- > **⚠️ Interim library do not invest in it as permanent.** It exists only
9
- > because GoA DS has not yet published an official Vue wrapper package. When
10
- > `@abgov/vue-components` ships, delete this library and repoint imports at it
7
+ | Folder | Contains | Lifespan |
8
+ |---|---|---|
9
+ | `src/lib/primitives/` | Thin `v-model`/idiomatic-event wrappers over individual `goa-*` elements (`GoabInput`, `GoabButton`, …) | **Interim** see below |
10
+ | `src/lib/patterns/` | Composite, app-shell components (`AppLayout`, `AppHeader`, `AppFooter`, `SessionExpiredBanner`) | **Permanent** |
11
+
12
+ > **⚠️ `primitives/` is interim — do not invest in it as permanent.** It exists
13
+ > only because GoA DS has not yet published an official Vue wrapper package. When
14
+ > `@abgov/vue-components` ships, delete `primitives/` and repoint imports at it —
11
15
  > the component names and props are deliberately kept matching, so it should be a
12
16
  > scope swap, not a rewrite. Don't add features here that would make that swap
13
17
  > harder (see "Don't" below).
18
+ >
19
+ > **`patterns/` is not part of that swap.** App-shell composition (layout,
20
+ > header, footer, banners) has no equivalent in an official design-system
21
+ > package — a design system ships primitives, not your app's shell. Nothing in
22
+ > `patterns/` is deleted when `primitives/` is.
14
23
 
15
24
  ## What these wrappers do (and why they're needed)
16
25
 
@@ -45,7 +54,7 @@ Most value elements put the value on `detail.value`; some carry extra keys
45
54
  (dropdown adds `label`, radio-group adds `labels`) you can ignore unless you need
46
55
  them. Always confirm against the component's page — see below.
47
56
 
48
- ## Wrapping a new component
57
+ ## Wrapping a new component (`primitives/`)
49
58
 
50
59
  ### 1. Find the event contract
51
60
 
@@ -129,14 +138,35 @@ Don't re-declare props you don't transform — let them fall through. `name` in
129
138
  particular is required by most `goa-*` form elements (it's echoed in the event
130
139
  detail); just leave it to fall through from the caller.
131
140
 
141
+ ## Building a pattern component (`patterns/`)
142
+
143
+ A pattern component is app-shell composition — layout, header/footer chrome,
144
+ banners — not a single-element wrapper. Existing examples: `AppLayout`,
145
+ `AppHeader`, `AppFooter`, `SessionExpiredBanner`.
146
+
147
+ - It's fine to compose `primitives/` wrappers inside a pattern component (e.g.
148
+ `SessionExpiredBanner` uses `GoabButton`) — import them with a relative path
149
+ (`../primitives/GoabButton.vue`), not the package's own public import path.
150
+ - Stay presentational: accept data and behavior via props/`v-model`/`emit`, don't
151
+ fetch data, call routing APIs, or encode business rules. A pattern component
152
+ used by every app in the workspace can't assume any one app's routes or
153
+ domain — that belongs in the app that uses it (see `SessionExpiredBanner`'s own
154
+ comment for a live example: the component renders the banner, but which
155
+ Keycloak hook flips `show` is main.ts's job, in the consuming app).
156
+ - `export` it from `src/index.ts` under the "patterns" group, with a one-line
157
+ purpose comment in this file's app-shell components table (mirrored in each
158
+ consuming generator's own `AGENTS.md`, e.g. `vue-app/files/AGENTS.md__tmpl__`).
159
+
132
160
  ## Don't
133
161
 
134
- - **Don't build net-new visual components or design primitives here.** That's GoA
135
- DS's job; this library is only *choreography* (adding `v-model`/idiomatic events
136
- to elements that already exist). New primitives would have no home in
137
- `@abgov/vue-components` and would block the swap.
138
- - **Don't add app-specific logic** (fetching, routing, business rules) — this is
139
- shared across every app in the workspace. Keep wrappers presentational.
162
+ - **Don't add new `goa-*` element wrappers to `patterns/`, or new composite
163
+ components to `primitives/`.** Single-element wrappers belong in
164
+ `primitives/` (interim, deleted on the `@abgov/vue-components` swap);
165
+ composites belong in `patterns/` (permanent). Putting one in the wrong folder
166
+ either blocks that swap or gets deleted by it.
167
+ - **Don't add app-specific logic** anywhere in this library (fetching, routing,
168
+ business rules) — it's shared across every app in the workspace. Keep every
169
+ component, wrapper or pattern, presentational.
140
170
 
141
171
  ## Testing
142
172
 
@@ -1,12 +1,23 @@
1
- // GoA Design System Vue wrappers INTERIM shared library.
2
- // These add v-model / idiomatic events over the framework-agnostic goa-* custom
3
- // elements. Remove this library and import from @abgov/vue-components once GoA DS
4
- // publishes it (see the repo's UX view-patterns spec, foundational gap #2) — the
5
- // component names/props are intended to match, so it should be a scope swap.
6
- export { default as GoabInput } from './lib/GoabInput.vue';
7
- export { default as GoabTextarea } from './lib/GoabTextarea.vue';
8
- export { default as GoabDropdown } from './lib/GoabDropdown.vue';
9
- export { default as GoabCheckbox } from './lib/GoabCheckbox.vue';
10
- export { default as GoabRadioGroup } from './lib/GoabRadioGroup.vue';
11
- export { default as GoabButton } from './lib/GoabButton.vue';
12
- export { default as GoabModal } from './lib/GoabModal.vue';
1
+ // GoA Design System Vue wrappers, shared across every Vue app in this workspace.
2
+ //
3
+ // primitives/ thin v-model/idiomatic-event wrappers over individual goa-*
4
+ // elements. INTERIM: once GoA DS publishes an official @abgov/vue-components,
5
+ // delete these files and repoint imports at it names/props are kept
6
+ // matching, so it's a scope swap, not a rewrite.
7
+ //
8
+ // patterns/ composite, app-shell components (layout, header, footer, ...).
9
+ // PERMANENT: these have no equivalent in an official design-system package
10
+ // (that's app-shell composition, not a design-system primitive) and are not
11
+ // deleted when primitives/ is.
12
+ export { default as GoabInput } from './lib/primitives/GoabInput.vue';
13
+ export { default as GoabTextarea } from './lib/primitives/GoabTextarea.vue';
14
+ export { default as GoabDropdown } from './lib/primitives/GoabDropdown.vue';
15
+ export { default as GoabCheckbox } from './lib/primitives/GoabCheckbox.vue';
16
+ export { default as GoabRadioGroup } from './lib/primitives/GoabRadioGroup.vue';
17
+ export { default as GoabButton } from './lib/primitives/GoabButton.vue';
18
+ export { default as GoabModal } from './lib/primitives/GoabModal.vue';
19
+
20
+ export { default as AppLayout } from './lib/patterns/AppLayout.vue';
21
+ export { default as AppHeader } from './lib/patterns/AppHeader.vue';
22
+ export { default as AppFooter } from './lib/patterns/AppFooter.vue';
23
+ export { default as SessionExpiredBanner } from './lib/patterns/SessionExpiredBanner.vue';
@@ -0,0 +1,14 @@
1
+ <template>
2
+ <goa-app-footer>
3
+ <goa-app-footer-nav-section slot="nav" heading="Resources">
4
+ <a href="https://alberta.ca/services.aspx">Services</a>
5
+ <a href="https://alberta.ca/contact.aspx">Contact</a>
6
+ <a href="https://alberta.ca/terms-of-use.aspx">Terms of Use</a>
7
+ </goa-app-footer-nav-section>
8
+ <goa-app-footer-meta-section slot="meta">
9
+ <a href="https://alberta.ca/security-and-privacy.aspx">Privacy</a>
10
+ <a href="https://alberta.ca/disclaimer.aspx">Disclaimer</a>
11
+ <a href="https://alberta.ca/accessibility.aspx">Accessibility</a>
12
+ </goa-app-footer-meta-section>
13
+ </goa-app-footer>
14
+ </template>
@@ -0,0 +1,19 @@
1
+ <script setup lang="ts">
2
+ import { useSlots } from 'vue';
3
+
4
+ defineProps<{ heading: string }>();
5
+ const slots = useSlots();
6
+ </script>
7
+
8
+ <template>
9
+ <goa-microsite-header type="alpha" />
10
+ <goa-app-header url="/" :heading="heading">
11
+ <!-- goa-app-header only renders content placed in a named slot — the
12
+ "utilities" slot is where GoA's own design system docs put
13
+ account/sign-in actions (as opposed to "navigation" for nav links).
14
+ A bare child with no slot attribute renders nothing. -->
15
+ <div v-if="slots.utilities" slot="utilities">
16
+ <slot name="utilities" />
17
+ </div>
18
+ </goa-app-header>
19
+ </template>
@@ -14,6 +14,7 @@ withDefaults(defineProps<{ variant?: 'page' | 'wide' | 'form' }>(), {
14
14
  </script>
15
15
 
16
16
  <template>
17
+ <a class="skip-link" href="#main-content">Skip to main content</a>
17
18
  <main id="main-content">
18
19
  <div :class="`${variant}-content`">
19
20
  <slot />
@@ -22,6 +23,22 @@ withDefaults(defineProps<{ variant?: 'page' | 'wide' | 'form' }>(), {
22
23
  </template>
23
24
 
24
25
  <style scoped>
26
+ /* Visually hidden until focused (e.g. via Tab from the top of the page) — lets
27
+ keyboard/screen-reader users jump past the header straight to the content. */
28
+ .skip-link {
29
+ position: absolute;
30
+ left: -9999px;
31
+ top: 0;
32
+ z-index: 100;
33
+ padding: var(--goa-space-s, 0.5rem) var(--goa-space-m, 1rem);
34
+ background: var(--goa-color-interactive-default, #0070c4);
35
+ color: #fff;
36
+ }
37
+
38
+ .skip-link:focus {
39
+ left: var(--goa-space-m, 1rem);
40
+ }
41
+
25
42
  .page-content,
26
43
  .wide-content,
27
44
  .form-content {
@@ -0,0 +1,28 @@
1
+ <script setup lang="ts">
2
+ // Presentational only — flipping `show` is the consuming app's job, not this
3
+ // component's. vue-app wires it to its session store's `expired` flag, itself
4
+ // set from main.ts's Keycloak plugin `onAuthRefreshError` hook — NOT
5
+ // `onAuthLogout`, which keycloak-js only fires via the session-status iframe
6
+ // (typically disabled, e.g. via checkLoginIframe: false) or Cordova mode, so
7
+ // it would never fire in a normal web deployment. onAuthRefreshError fires
8
+ // from the wrapper's own background auto-refresh failing because the refresh
9
+ // token itself expired — the real, always-on signal.
10
+ import GoabButton from '../primitives/GoabButton.vue';
11
+
12
+ const show = defineModel<boolean>('show', { default: false });
13
+ const emit = defineEmits<{ signIn: []; dismiss: [] }>();
14
+ </script>
15
+
16
+ <template>
17
+ <div v-if="show" class="session-expired-banner">
18
+ <goa-callout type="emergency" heading="Session expired">
19
+ <p>Your session has expired. Sign in again to continue.</p>
20
+ <goa-spacer vspacing="s" />
21
+ <goa-button-group gap="relaxed">
22
+ <GoabButton type="primary" @click="emit('signIn')">Sign in</GoabButton>
23
+ <GoabButton type="tertiary" @click="emit('dismiss')">Dismiss</GoabButton>
24
+ </goa-button-group>
25
+ </goa-callout>
26
+ <goa-spacer vspacing="m" />
27
+ </div>
28
+ </template>
@@ -1,11 +1,11 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import * as lib from './index';
3
3
 
4
- // Smoke test: the barrel resolves and every wrapper SFC compiles under the test
5
- // runner. It also gives the library a test target with real content, so
6
- // `nx test vue-components` doesn't fail on "no test files found".
4
+ // Smoke test: the barrel resolves and every wrapper/pattern SFC compiles under
5
+ // the test runner. It also gives the library a test target with real content,
6
+ // so `nx test vue-components` doesn't fail on "no test files found".
7
7
  describe('vue-components', () => {
8
- it('exports every GoA wrapper', () => {
8
+ it('exports every GoA primitive wrapper', () => {
9
9
  for (const name of [
10
10
  'GoabInput',
11
11
  'GoabTextarea',
@@ -18,4 +18,15 @@ describe('vue-components', () => {
18
18
  expect(lib[name as keyof typeof lib]).toBeTruthy();
19
19
  }
20
20
  });
21
+
22
+ it('exports every app-shell pattern component', () => {
23
+ for (const name of [
24
+ 'AppLayout',
25
+ 'AppHeader',
26
+ 'AppFooter',
27
+ 'SessionExpiredBanner',
28
+ ]) {
29
+ expect(lib[name as keyof typeof lib]).toBeTruthy();
30
+ }
31
+ });
21
32
  });
@@ -23,7 +23,7 @@ describe('Vue Components Generator', () => {
23
23
  const config = readProjectConfiguration(host, 'vue-components');
24
24
  expect(config.root).toBe('libs/vue-components');
25
25
 
26
- const base = 'libs/vue-components/src/lib';
26
+ const primitives = 'libs/vue-components/src/lib/primitives';
27
27
  for (const name of [
28
28
  'GoabInput',
29
29
  'GoabTextarea',
@@ -33,10 +33,21 @@ describe('Vue Components Generator', () => {
33
33
  'GoabButton',
34
34
  'GoabModal',
35
35
  ]) {
36
- expect(host.exists(`${base}/${name}.vue`)).toBeTruthy();
36
+ expect(host.exists(`${primitives}/${name}.vue`)).toBeTruthy();
37
+ }
38
+ // Permanent app-shell pattern components live alongside the interim wrappers.
39
+ const patterns = 'libs/vue-components/src/lib/patterns';
40
+ for (const name of [
41
+ 'AppLayout',
42
+ 'AppHeader',
43
+ 'AppFooter',
44
+ 'SessionExpiredBanner',
45
+ ]) {
46
+ expect(host.exists(`${patterns}/${name}.vue`)).toBeTruthy();
37
47
  }
38
48
  const index = host.read('libs/vue-components/src/index.ts').toString();
39
49
  expect(index).toContain('export { default as GoabInput }');
50
+ expect(index).toContain('export { default as AppLayout }');
40
51
  expect(index).toContain('@abgov/vue-components'); // interim marker
41
52
 
42
53
  // Ships a spec so the vitest test target isn't empty (vitest exits non-zero
@@ -79,7 +90,7 @@ describe('Vue Components Generator', () => {
79
90
  await generator(host);
80
91
  await expect(generator(host)).resolves.not.toThrow();
81
92
  expect(
82
- host.exists('libs/vue-components/src/lib/GoabInput.vue'),
93
+ host.exists('libs/vue-components/src/lib/primitives/GoabInput.vue'),
83
94
  ).toBeTruthy();
84
95
  }, 30000);
85
96