@abgov/nx-adsp 13.30.0 → 13.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abgov/nx-adsp",
3
- "version": "13.30.0",
3
+ "version": "13.31.0",
4
4
  "license": "Apache-2.0",
5
5
  "main": "src/index.js",
6
6
  "description": "Government of Alberta - Nx plugin for ADSP apps.",
@@ -78,6 +78,13 @@ function onItemClick(item: { to?: string }) {
78
78
  </AppLayout>
79
79
  </AppSideMenu>
80
80
  <% } else { %>
81
+ <!-- goa-one-column-layout owns the page's flex column, the sticky footer and
82
+ the <main> landmark. That <main> lives in the element's shadow DOM, so the
83
+ skip link targets an id'd wrapper in the slotted (light DOM) content
84
+ instead -- the wrapper is a plain div, so there is still exactly one main
85
+ landmark on the page. -->
86
+ <goa-one-column-layout>
87
+ <section slot="header">
81
88
  <a class="skip-link" href="#main-content">Skip to main content</a>
82
89
  <AppHeader heading="<%= projectName %>">
83
90
  <template #utilities>
@@ -100,12 +107,18 @@ function onItemClick(item: { to?: string }) {
100
107
  @sign-in="signInAgain"
101
108
  @dismiss="session.dismiss"
102
109
  />
103
- <main id="main-content">
110
+ </section>
111
+
112
+ <div id="main-content">
104
113
  <AppLayout :variant="contentWidth">
105
114
  <RouterView />
106
115
  </AppLayout>
107
- </main>
108
- <AppFooter />
116
+ </div>
117
+
118
+ <section slot="footer">
119
+ <AppFooter />
120
+ </section>
121
+ </goa-one-column-layout>
109
122
  <% } %>
110
123
  </template>
111
124
 
@@ -120,10 +120,17 @@ describe('Vue App Generator', () => {
120
120
  const layoutPath = 'libs/vue-components/src/lib/patterns/AppLayout.vue';
121
121
  expect(host.exists(layoutPath)).toBeTruthy();
122
122
  const layout = host.read(layoutPath).toString();
123
- // Three named width variants, token-driven padding.
124
- expect(layout).toContain('form-content');
125
- expect(layout).toContain('wide-content');
123
+ // The gutter is goa-page-block's job now: it supplies the centering, the
124
+ // max-width and a responsive horizontal gutter. The named variants stay the
125
+ // public API, mapped to the widths the element takes.
126
+ expect(layout).toContain('<goa-page-block');
127
+ expect(layout).toContain("form: '640px'");
128
+ expect(layout).toContain("page: '1000px'");
129
+ expect(layout).toContain("wide: '1200px'");
130
+ // Vertical padding is not part of goa-page-block, so this component keeps it
131
+ // -- the same thing GoA's own public-form reference does.
126
132
  expect(layout).toContain('--goa-space');
133
+ expect(layout).toContain('padding-block');
127
134
  // AppLayout must NOT own the skip-to-main-content landmark itself: it nests
128
135
  // inside AppSideMenu for --layout=internal, which already provides one, and
129
136
  // a second <main id="main-content"> would duplicate the landmark/id.
@@ -137,8 +144,16 @@ describe('Vue App Generator', () => {
137
144
  const app = host.read('apps/test/src/App.vue').toString();
138
145
  expect(app).toContain('AppLayout');
139
146
  expect(app).not.toContain('main > section');
147
+ // The public shell is goa-one-column-layout, which owns the page's flex
148
+ // column, sticky footer and <main> landmark. That <main> is in its shadow
149
+ // DOM, so the skip link targets an id'd wrapper in the slotted content --
150
+ // a plain div, so the page still has exactly one main landmark.
151
+ expect(app).toContain('<goa-one-column-layout>');
152
+ expect(app).toContain('<section slot="header">');
153
+ expect(app).toContain('<section slot="footer">');
140
154
  expect(app).toContain('href="#main-content"');
141
- expect(app).toContain('id="main-content"');
155
+ expect(app).toContain('<div id="main-content">');
156
+ expect(app).not.toContain('<main id="main-content">');
142
157
  });
143
158
 
144
159
  it('provisions the shared GoA wrapper library and points the app at it', async () => {
@@ -0,0 +1,32 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import AppLayout from './AppLayout.vue';
4
+
5
+ // Note on how width is asserted: in this test environment goa-page-block is not
6
+ // a defined custom element, so Vue falls back to setting the width as an
7
+ // ATTRIBUTE. In a real browser the element defines the property, so Vue sets it
8
+ // as a property and getAttribute('width') returns null -- verified against a
9
+ // running app, where the element then applies --max-width correctly. These tests
10
+ // therefore guard the variant -> width mapping, which is the part that can
11
+ // regress; they are not evidence about attribute-vs-property binding.
12
+ describe('AppLayout', () => {
13
+ const widthFor = (variant?: 'page' | 'wide' | 'form') =>
14
+ mount(AppLayout, { props: variant ? { variant } : {} })
15
+ .find('goa-page-block')
16
+ .attributes('width');
17
+
18
+ it('delegates the content gutter to goa-page-block', () => {
19
+ expect(mount(AppLayout).find('goa-page-block').exists()).toBe(true);
20
+ });
21
+
22
+ it('maps each variant to the width the element expects', () => {
23
+ expect(widthFor('form')).toBe('640px');
24
+ expect(widthFor()).toBe('1000px');
25
+ expect(widthFor('wide')).toBe('1200px');
26
+ });
27
+
28
+ it('renders slotted content inside the block', () => {
29
+ const w = mount(AppLayout, { slots: { default: '<p>hello</p>' } });
30
+ expect(w.find('goa-page-block').html()).toContain('<p>hello</p>');
31
+ });
32
+ });
@@ -14,36 +14,43 @@
14
14
  // copy would duplicate the anchor screen readers/keyboard nav jump to. The
15
15
  // top-level shell (App.vue for --layout=header, AppSideMenu for --layout=internal)
16
16
  // owns that landmark instead.
17
- withDefaults(defineProps<{ variant?: 'page' | 'wide' | 'form' }>(), {
18
- variant: 'page',
19
- });
17
+ import { computed } from 'vue';
18
+
19
+ const props = withDefaults(
20
+ defineProps<{ variant?: 'page' | 'wide' | 'form' }>(),
21
+ { variant: 'page' },
22
+ );
23
+
24
+ // goa-page-block takes a CSS dimension (or "full"); the variant names stay the
25
+ // public API so a view says what kind of page it is, not how wide it should be.
26
+ const WIDTHS = {
27
+ form: '640px',
28
+ page: '1000px',
29
+ wide: '1200px',
30
+ } as const;
31
+
32
+ const maxWidth = computed(() => WIDTHS[props.variant]);
20
33
  </script>
21
34
 
22
35
  <template>
23
- <div :class="`${variant}-content`">
24
- <slot />
25
- </div>
36
+ <goa-page-block :width="maxWidth">
37
+ <!-- goa-page-block supplies the centering, the max-width and a responsive
38
+ HORIZONTAL gutter, but no vertical padding -- GoA's own public-form
39
+ reference does the same thing, wrapping the block's children to add it. -->
40
+ <div class="app-layout-inset">
41
+ <slot />
42
+ </div>
43
+ </goa-page-block>
26
44
  </template>
27
45
 
28
46
  <style scoped>
29
- .page-content,
30
- .wide-content,
31
- .form-content {
32
- margin-inline: auto;
33
- box-sizing: border-box;
34
- /* Design-token gutter; fallbacks apply only if tokens aren't loaded. */
35
- padding: var(--goa-space-xl, 2.5rem) var(--goa-space-l, 1.5rem);
47
+ .app-layout-inset {
48
+ padding-block: var(--goa-space-xl, 2.5rem);
36
49
  }
37
50
 
38
- .form-content { max-width: 640px; }
39
- .page-content { max-width: 1000px; }
40
- .wide-content { max-width: 1200px; }
41
-
42
51
  @media (max-width: 623.98px) {
43
- .page-content,
44
- .wide-content,
45
- .form-content {
46
- padding: var(--goa-space-m, 1rem);
52
+ .app-layout-inset {
53
+ padding-block: var(--goa-space-m, 1rem);
47
54
  }
48
55
  }
49
56
  </style>
@@ -171,6 +171,7 @@ describe('Vue Components Generator', () => {
171
171
  'libs/vue-components/src/lib/primitives/GoabDatePicker.spec.ts',
172
172
  'libs/vue-components/src/lib/patterns/WorkspaceTable.spec.ts',
173
173
  'libs/vue-components/src/lib/patterns/Stepper.spec.ts',
174
+ 'libs/vue-components/src/lib/patterns/AppLayout.spec.ts',
174
175
  ]) {
175
176
  expect(host.exists(spec)).toBeTruthy();
176
177
  }