weg-shared-layout 0.0.24 → 0.0.25
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/dist/cjs/weg-footer_2.cjs.entry.js +218 -193
- package/dist/collection/components/weg-footer/weg-footer.css +97 -45
- package/dist/collection/components/weg-footer/weg-footer.js +22 -26
- package/dist/collection/components/weg-header/weg-header.css +33 -32
- package/dist/collection/components/weg-header/weg-header.js +142 -138
- package/dist/collection/styles/shared.css +3 -3
- package/dist/collection/utils/layout.js +57 -9
- package/dist/components/p-BnMIy-A5.js +1 -0
- package/dist/components/weg-footer.js +1 -1
- package/dist/components/weg-header.js +1 -1
- package/dist/esm/weg-footer_2.entry.js +218 -193
- package/dist/types/components/weg-footer/weg-footer.d.ts +2 -2
- package/dist/types/components/weg-header/weg-header.d.ts +14 -11
- package/dist/types/components.d.ts +6 -6
- package/dist/types/types/layout-data.d.ts +13 -27
- package/dist/types/utils/layout.d.ts +9 -1
- package/dist/weg-shared-layout/p-d74a58cf.entry.js +1 -0
- package/dist/weg-shared-layout/weg-shared-layout.esm.js +1 -1
- package/docs/angular-integration-guide.md +81 -103
- package/docs/angular.md +9 -27
- package/docs/nextjs.md +11 -10
- package/docs/react.md +3 -3
- package/docs/vanilla.md +8 -10
- package/package.json +1 -1
- package/readme.md +16 -42
- package/src/assets/dummy-data.json +221 -147
- package/dist/collection/components/weg-header/signed-in-layout.js +0 -13
- package/dist/collection/constants/weg-urls.js +0 -18
- package/dist/components/p-DbIEJ3IT.js +0 -1
- package/dist/types/components/weg-header/signed-in-layout.d.ts +0 -18
- package/dist/types/constants/weg-urls.d.ts +0 -18
- package/dist/weg-shared-layout/p-c6ec3888.entry.js +0 -1
- package/src/constants/weg-urls.ts +0 -23
|
@@ -51,7 +51,7 @@ Copy this list while integrating:
|
|
|
51
51
|
| Tag | Purpose |
|
|
52
52
|
| --- | --- |
|
|
53
53
|
| `<weg-header>` | Header — bundled logo, CMS nav (signed out), built-in nav (signed in), Sign in / Manage Account / Sign out |
|
|
54
|
-
| `<weg-footer>` | Footer — social links,
|
|
54
|
+
| `<weg-footer>` | Footer — social links, menu, credits, copyright |
|
|
55
55
|
|
|
56
56
|
They are **presentational**: they **do not** fetch data. Your Angular app loads layout JSON and passes it in.
|
|
57
57
|
|
|
@@ -156,8 +156,6 @@ Create `src/app/auth.ts`. Host apps own sign-in/out URLs — do not import auth
|
|
|
156
156
|
|
|
157
157
|
```ts
|
|
158
158
|
// src/app/auth.ts
|
|
159
|
-
export const HEADER_LOGO_HREF = 'https://www.warwickemploymentgroup.com/';
|
|
160
|
-
|
|
161
159
|
export const HEADER_SIGN_IN = {
|
|
162
160
|
label: 'Sign in',
|
|
163
161
|
href: 'https://account.warwickemploymentgroup.com/account/login',
|
|
@@ -167,7 +165,7 @@ export const HEADER_SIGN_IN = {
|
|
|
167
165
|
export const ACCOUNT_LOGIN_HREF = HEADER_SIGN_IN.href;
|
|
168
166
|
```
|
|
169
167
|
|
|
170
|
-
|
|
168
|
+
If your API omits a Sign in entry in `header.menu`, append `HEADER_SIGN_IN` when building layout — see Step 7.
|
|
171
169
|
|
|
172
170
|
---
|
|
173
171
|
|
|
@@ -210,7 +208,7 @@ import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
|
210
208
|
import { RouterOutlet } from '@angular/router';
|
|
211
209
|
import layoutFixture from 'weg-shared-layout/dummy-data.json';
|
|
212
210
|
|
|
213
|
-
import { ACCOUNT_LOGIN_HREF,
|
|
211
|
+
import { ACCOUNT_LOGIN_HREF, HEADER_SIGN_IN } from './auth';
|
|
214
212
|
import type { LayoutData } from './layout.types';
|
|
215
213
|
|
|
216
214
|
@Component({
|
|
@@ -221,14 +219,7 @@ import type { LayoutData } from './layout.types';
|
|
|
221
219
|
})
|
|
222
220
|
export class App {
|
|
223
221
|
/** Same object for header and footer */
|
|
224
|
-
layoutData: LayoutData =
|
|
225
|
-
...layoutFixture,
|
|
226
|
-
header: {
|
|
227
|
-
...layoutFixture.header,
|
|
228
|
-
logoHref: HEADER_LOGO_HREF,
|
|
229
|
-
signIn: HEADER_SIGN_IN,
|
|
230
|
-
},
|
|
231
|
-
};
|
|
222
|
+
layoutData: LayoutData = layoutFixture;
|
|
232
223
|
|
|
233
224
|
signedIn = false;
|
|
234
225
|
userName?: string;
|
|
@@ -243,8 +234,7 @@ export class App {
|
|
|
243
234
|
return;
|
|
244
235
|
}
|
|
245
236
|
|
|
246
|
-
window.location.href =
|
|
247
|
-
this.layoutData.header?.signIn?.href ?? HEADER_SIGN_IN.href;
|
|
237
|
+
window.location.href = HEADER_SIGN_IN.href;
|
|
248
238
|
}
|
|
249
239
|
}
|
|
250
240
|
```
|
|
@@ -287,7 +277,7 @@ import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';
|
|
|
287
277
|
import { RouterOutlet } from '@angular/router';
|
|
288
278
|
import layoutFixture from 'weg-shared-layout/dummy-data.json';
|
|
289
279
|
|
|
290
|
-
import { ACCOUNT_LOGIN_HREF,
|
|
280
|
+
import { ACCOUNT_LOGIN_HREF, HEADER_SIGN_IN } from './auth';
|
|
291
281
|
import type { LayoutData } from './layout.types';
|
|
292
282
|
|
|
293
283
|
@Component({
|
|
@@ -297,14 +287,7 @@ import type { LayoutData } from './layout.types';
|
|
|
297
287
|
templateUrl: './app.html',
|
|
298
288
|
})
|
|
299
289
|
export class App {
|
|
300
|
-
readonly layoutData = signal<LayoutData>(
|
|
301
|
-
...layoutFixture,
|
|
302
|
-
header: {
|
|
303
|
-
...layoutFixture.header,
|
|
304
|
-
logoHref: HEADER_LOGO_HREF,
|
|
305
|
-
signIn: HEADER_SIGN_IN,
|
|
306
|
-
},
|
|
307
|
-
});
|
|
290
|
+
readonly layoutData = signal<LayoutData>(layoutFixture);
|
|
308
291
|
|
|
309
292
|
readonly signedIn = signal(false);
|
|
310
293
|
readonly userName = signal<string | undefined>(undefined);
|
|
@@ -320,8 +303,7 @@ export class App {
|
|
|
320
303
|
return;
|
|
321
304
|
}
|
|
322
305
|
|
|
323
|
-
window.location.href =
|
|
324
|
-
this.layoutData().header?.signIn?.href ?? HEADER_SIGN_IN.href;
|
|
306
|
+
window.location.href = HEADER_SIGN_IN.href;
|
|
325
307
|
}
|
|
326
308
|
}
|
|
327
309
|
```
|
|
@@ -358,7 +340,7 @@ import { Injectable } from '@angular/core';
|
|
|
358
340
|
import { HttpClient } from '@angular/common/http';
|
|
359
341
|
import { map, Observable } from 'rxjs';
|
|
360
342
|
|
|
361
|
-
import {
|
|
343
|
+
import { HEADER_SIGN_IN } from './auth';
|
|
362
344
|
import type { LayoutData } from './layout.types';
|
|
363
345
|
|
|
364
346
|
@Injectable({ providedIn: 'root' })
|
|
@@ -368,18 +350,21 @@ export class LayoutService {
|
|
|
368
350
|
/** CMS returns full { header, footer } object */
|
|
369
351
|
loadLayout(): Observable<LayoutData> {
|
|
370
352
|
return this.http.get<LayoutData>('/api/layout').pipe(
|
|
371
|
-
map((data) => this.
|
|
353
|
+
map((data) => this.ensureSignInMenu(data)),
|
|
372
354
|
);
|
|
373
355
|
}
|
|
374
356
|
|
|
375
|
-
private
|
|
357
|
+
private ensureSignInMenu(data: LayoutData): LayoutData {
|
|
358
|
+
const menu = data.header?.menu ?? [];
|
|
359
|
+
const hasSignIn = menu.some(
|
|
360
|
+
(item) =>
|
|
361
|
+
!item.items?.length &&
|
|
362
|
+
item.label?.trim().toLowerCase() === 'sign in',
|
|
363
|
+
);
|
|
364
|
+
if (hasSignIn) return data;
|
|
376
365
|
return {
|
|
377
366
|
...data,
|
|
378
|
-
header: {
|
|
379
|
-
...data.header,
|
|
380
|
-
logoHref: HEADER_LOGO_HREF,
|
|
381
|
-
signIn: HEADER_SIGN_IN,
|
|
382
|
-
},
|
|
367
|
+
header: { ...data.header, menu: [...menu, HEADER_SIGN_IN] },
|
|
383
368
|
};
|
|
384
369
|
}
|
|
385
370
|
}
|
|
@@ -527,14 +512,14 @@ export class App {
|
|
|
527
512
|
| Binding | Web component | Purpose |
|
|
528
513
|
| --- | --- | --- |
|
|
529
514
|
| `[layout]="..."` | `layout` | CMS nav when **signed out** |
|
|
530
|
-
| `[signedIn]="..."` | `signed-in` | `true` → built-in signed-in nav (ignores CMS
|
|
515
|
+
| `[signedIn]="..."` | `signed-in` | `true` → built-in signed-in nav (ignores CMS `header.menu`) |
|
|
531
516
|
| `[userName]="..."` | `user-name` | First name on Manage Account when signed in |
|
|
532
517
|
| `(wegAuthClick)="..."` | `wegAuthClick` | `detail.action`: `'sign-in'` \| `'sign-out'` |
|
|
533
518
|
|
|
534
519
|
**Signed out** (`signedIn === false`):
|
|
535
520
|
|
|
536
|
-
- Uses `layout.header.
|
|
537
|
-
- Logo
|
|
521
|
+
- Uses `layout.header.menu` — dropdown groups (`items`) and flat links (`href`), including Sign in
|
|
522
|
+
- Logo **image** from `layout.header.logoSrc` (bundled if omitted); logo **link** always goes to WEG home
|
|
538
523
|
|
|
539
524
|
**Signed in** (`signedIn === true`):
|
|
540
525
|
|
|
@@ -570,19 +555,16 @@ Both tags accept one **`layout`** object with optional `header` and `footer` key
|
|
|
570
555
|
### TypeScript shape
|
|
571
556
|
|
|
572
557
|
```ts
|
|
573
|
-
type LayoutLink = {
|
|
574
|
-
|
|
575
|
-
type LayoutHeaderDropdown = {
|
|
558
|
+
type LayoutLink = {
|
|
576
559
|
label: string;
|
|
577
|
-
|
|
560
|
+
href?: string;
|
|
561
|
+
target?: '_blank' | '_self' | '_parent' | '_top';
|
|
562
|
+
items?: LayoutLink[];
|
|
578
563
|
};
|
|
579
564
|
|
|
580
565
|
type LayoutHeaderData = {
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
links?: LayoutLink[];
|
|
584
|
-
signIn?: LayoutLink;
|
|
585
|
-
signOut?: { label: string; href?: string };
|
|
566
|
+
logoSrc?: string;
|
|
567
|
+
menu?: LayoutLink[];
|
|
586
568
|
};
|
|
587
569
|
|
|
588
570
|
type LayoutFooterSocialLink = {
|
|
@@ -590,18 +572,16 @@ type LayoutFooterSocialLink = {
|
|
|
590
572
|
href: string;
|
|
591
573
|
};
|
|
592
574
|
|
|
593
|
-
type
|
|
594
|
-
|
|
575
|
+
type LayoutFooterData = {
|
|
576
|
+
social?: LayoutFooterSocialLink[];
|
|
577
|
+
menu?: LayoutLink[][];
|
|
578
|
+
credits?: string;
|
|
579
|
+
copyright?: string;
|
|
595
580
|
};
|
|
596
581
|
|
|
597
582
|
type LayoutData = {
|
|
598
583
|
header?: Partial<LayoutHeaderData>;
|
|
599
|
-
footer?: Partial<
|
|
600
|
-
social: LayoutFooterSocialLink[];
|
|
601
|
-
columns: LayoutFooterColumn[];
|
|
602
|
-
credits: string;
|
|
603
|
-
copyright: string;
|
|
604
|
-
}>;
|
|
584
|
+
footer?: Partial<LayoutFooterData>;
|
|
605
585
|
};
|
|
606
586
|
```
|
|
607
587
|
|
|
@@ -616,27 +596,25 @@ Full example JSON:
|
|
|
616
596
|
```json
|
|
617
597
|
{
|
|
618
598
|
"header": {
|
|
619
|
-
"
|
|
620
|
-
"
|
|
599
|
+
"logoSrc": "https://www.warwickemploymentgroup.com/assets/weg-logo.svg",
|
|
600
|
+
"menu": [
|
|
621
601
|
{
|
|
622
602
|
"label": "Find a job",
|
|
623
603
|
"items": [
|
|
624
604
|
{ "label": "Graduates", "href": "https://example.com/jobs/graduates" }
|
|
625
605
|
]
|
|
626
|
-
}
|
|
627
|
-
],
|
|
628
|
-
"links": [
|
|
606
|
+
},
|
|
629
607
|
{ "label": "Career advice", "href": "https://example.com/career-advice" },
|
|
630
|
-
{ "label": "Register", "href": "https://example.com/register" }
|
|
631
|
-
|
|
632
|
-
|
|
608
|
+
{ "label": "Register", "href": "https://example.com/register" },
|
|
609
|
+
{ "label": "Sign in", "href": "https://example.com/login" }
|
|
610
|
+
]
|
|
633
611
|
},
|
|
634
612
|
"footer": {
|
|
635
613
|
"social": [
|
|
636
614
|
{ "platform": "LinkedIn", "href": "https://linkedin.com/company/example" }
|
|
637
615
|
],
|
|
638
|
-
"
|
|
639
|
-
|
|
616
|
+
"menu": [
|
|
617
|
+
[{ "label": "About WEG", "href": "/about" }]
|
|
640
618
|
],
|
|
641
619
|
"credits": "Warwick Employment Group …",
|
|
642
620
|
"copyright": "Copyright © Warwick Employment Group."
|
|
@@ -648,17 +626,14 @@ Full example JSON:
|
|
|
648
626
|
|
|
649
627
|
| Field | Used when | Notes |
|
|
650
628
|
| --- | --- | --- |
|
|
651
|
-
| `header.
|
|
652
|
-
| `header.
|
|
653
|
-
| `header.links` | Signed **out** | Flat nav links |
|
|
654
|
-
| `header.signIn` | Signed **out** | Sign in button |
|
|
655
|
-
| `header.signOut` | Rarely | Signed-in sign-out uses built-in URLs |
|
|
629
|
+
| `header.logoSrc` | Signed **out** | Logo image URL; bundled if omitted |
|
|
630
|
+
| `header.menu` | Signed **out** | Unified nav — groups use `items[]`, flat links use `href` |
|
|
656
631
|
| `footer.social` | Always | `platform` must be `LinkedIn`, `Instagram`, `TikTok`, or `YouTube` exactly |
|
|
657
|
-
| `footer.
|
|
658
|
-
| `footer.credits` | Always | Text below
|
|
632
|
+
| `footer.menu` | Always | `LayoutLink[][]` — one link array per column |
|
|
633
|
+
| `footer.credits` | Always | Text below menu |
|
|
659
634
|
| `footer.copyright` | Always | Copyright line |
|
|
660
635
|
|
|
661
|
-
When **`signedIn` is true**, `<weg-header>` **ignores** `
|
|
636
|
+
When **`signedIn` is true**, `<weg-header>` **ignores** `header.menu` from layout.
|
|
662
637
|
|
|
663
638
|
### Property binding vs attributes
|
|
664
639
|
|
|
@@ -675,7 +650,7 @@ The component can parse a JSON **string** for `layout`, but `[layout]="object"`
|
|
|
675
650
|
### Invalid entries are silently dropped
|
|
676
651
|
|
|
677
652
|
- Links without `label` or `href` are skipped
|
|
678
|
-
-
|
|
653
|
+
- Menu groups with empty `items` are skipped
|
|
679
654
|
- Unknown social `platform` values are skipped
|
|
680
655
|
- Malformed input → empty sections (no throw) — inspect `$0.layout` in DevTools
|
|
681
656
|
|
|
@@ -703,19 +678,18 @@ const navLinks = [
|
|
|
703
678
|
|
|
704
679
|
| Field | Array element shape |
|
|
705
680
|
| --- | --- |
|
|
706
|
-
| `header.
|
|
707
|
-
| `header.links` | `{ label, href }` |
|
|
681
|
+
| `header.menu` | `LayoutLink` — group with `items[]` or flat link with `href` |
|
|
708
682
|
| `footer.social` | `{ platform, href }` |
|
|
709
|
-
| `footer.
|
|
683
|
+
| `footer.menu` | `{ label, href }[]` per column |
|
|
710
684
|
|
|
711
|
-
**`src/app/layout.service.ts` — merge multiple endpoints:**
|
|
685
|
+
**`src/app/layout.service.ts` — merge multiple endpoints into one menu:**
|
|
712
686
|
|
|
713
687
|
```ts
|
|
714
688
|
import { Injectable } from '@angular/core';
|
|
715
689
|
import { HttpClient } from '@angular/common/http';
|
|
716
690
|
import { forkJoin, map, Observable } from 'rxjs';
|
|
717
691
|
|
|
718
|
-
import {
|
|
692
|
+
import { HEADER_SIGN_IN } from './auth';
|
|
719
693
|
import type { LayoutData } from './layout.types';
|
|
720
694
|
|
|
721
695
|
@Injectable({ providedIn: 'root' })
|
|
@@ -724,18 +698,17 @@ export class LayoutService {
|
|
|
724
698
|
|
|
725
699
|
loadLayoutFromParts(): Observable<LayoutData> {
|
|
726
700
|
return forkJoin({
|
|
727
|
-
|
|
728
|
-
'/api/nav/
|
|
701
|
+
menuGroups: this.http.get<NonNullable<LayoutData['header']>['menu']>(
|
|
702
|
+
'/api/nav/menu',
|
|
703
|
+
),
|
|
704
|
+
flatLinks: this.http.get<NonNullable<LayoutData['header']>['menu']>(
|
|
705
|
+
'/api/nav/links',
|
|
729
706
|
),
|
|
730
|
-
links: this.http.get<NonNullable<LayoutData['header']>['links']>('/api/nav/links'),
|
|
731
707
|
footer: this.http.get<LayoutData['footer']>('/api/footer'),
|
|
732
708
|
}).pipe(
|
|
733
|
-
map(({
|
|
709
|
+
map(({ menuGroups, flatLinks, footer }) => ({
|
|
734
710
|
header: {
|
|
735
|
-
|
|
736
|
-
dropdowns,
|
|
737
|
-
links,
|
|
738
|
-
signIn: HEADER_SIGN_IN,
|
|
711
|
+
menu: [...menuGroups, ...flatLinks, HEADER_SIGN_IN],
|
|
739
712
|
},
|
|
740
713
|
footer,
|
|
741
714
|
})),
|
|
@@ -771,29 +744,30 @@ import { Injectable } from '@angular/core';
|
|
|
771
744
|
import { BehaviorSubject } from 'rxjs';
|
|
772
745
|
import layoutFixture from 'weg-shared-layout/dummy-data.json';
|
|
773
746
|
|
|
774
|
-
import {
|
|
747
|
+
import { HEADER_SIGN_IN } from './auth';
|
|
775
748
|
import type { LayoutData } from './layout.types';
|
|
776
749
|
|
|
777
750
|
@Injectable({ providedIn: 'root' })
|
|
778
751
|
export class LayoutService {
|
|
779
|
-
private readonly layoutSubject = new BehaviorSubject(
|
|
780
|
-
this.withAppHeaderOverrides(layoutFixture),
|
|
781
|
-
);
|
|
752
|
+
private readonly layoutSubject = new BehaviorSubject<LayoutData>(layoutFixture);
|
|
782
753
|
|
|
783
754
|
readonly layout$ = this.layoutSubject.asObservable();
|
|
784
755
|
|
|
785
756
|
setLayout(data: LayoutData): void {
|
|
786
|
-
this.layoutSubject.next(this.
|
|
757
|
+
this.layoutSubject.next(this.ensureSignInMenu(data));
|
|
787
758
|
}
|
|
788
759
|
|
|
789
|
-
private
|
|
760
|
+
private ensureSignInMenu(data: LayoutData): LayoutData {
|
|
761
|
+
const menu = data.header?.menu ?? [];
|
|
762
|
+
const hasSignIn = menu.some(
|
|
763
|
+
(item) =>
|
|
764
|
+
!item.items?.length &&
|
|
765
|
+
item.label?.trim().toLowerCase() === 'sign in',
|
|
766
|
+
);
|
|
767
|
+
if (hasSignIn) return data;
|
|
790
768
|
return {
|
|
791
769
|
...data,
|
|
792
|
-
header: {
|
|
793
|
-
...data.header,
|
|
794
|
-
logoHref: HEADER_LOGO_HREF,
|
|
795
|
-
signIn: HEADER_SIGN_IN,
|
|
796
|
-
},
|
|
770
|
+
header: { ...data.header, menu: [...menu, HEADER_SIGN_IN] },
|
|
797
771
|
};
|
|
798
772
|
}
|
|
799
773
|
}
|
|
@@ -809,11 +783,11 @@ Stencil watches the `layout` **property**. Assign a **new object reference** whe
|
|
|
809
783
|
// Good
|
|
810
784
|
this.layoutData = {
|
|
811
785
|
...this.layoutData,
|
|
812
|
-
header: { ...this.layoutData.header,
|
|
786
|
+
header: { ...this.layoutData.header, menu: newMenu },
|
|
813
787
|
};
|
|
814
788
|
|
|
815
789
|
// Risky — may not re-render
|
|
816
|
-
this.layoutData.header!.
|
|
790
|
+
this.layoutData.header!.menu!.push(newLink);
|
|
817
791
|
```
|
|
818
792
|
|
|
819
793
|
With `OnPush` and plain fields, call `ChangeDetectorRef.markForCheck()` after updates.
|
|
@@ -926,7 +900,11 @@ const header = fixture.nativeElement.querySelector('weg-header') as HTMLElement
|
|
|
926
900
|
layout?: LayoutData;
|
|
927
901
|
};
|
|
928
902
|
|
|
929
|
-
expect(
|
|
903
|
+
expect(
|
|
904
|
+
header.layout?.header?.menu?.some(
|
|
905
|
+
(item) => item.label === 'Sign in' && item.href === HEADER_SIGN_IN.href,
|
|
906
|
+
),
|
|
907
|
+
).toBe(true);
|
|
930
908
|
```
|
|
931
909
|
|
|
932
910
|
Full example: [weg-angular-demo `app.spec.ts`](https://github.com/jobsac/weg-angular-demo/blob/main/src/app/app.spec.ts).
|
|
@@ -968,7 +946,7 @@ External `https://` links open in a new tab automatically.
|
|
|
968
946
|
| `'weg-header' is not a known element` | Missing schema | `CUSTOM_ELEMENTS_SCHEMA` on the component that owns the template |
|
|
969
947
|
| Header/footer completely empty | Not registered | `defineCustomElements()` before bootstrap, or tag side-effect imports |
|
|
970
948
|
| Tags render, nav empty | Wrong `layout` | `[layout]="..."` with object `{ header?, footer? }` |
|
|
971
|
-
| Passed array to `[layout]` | Top-level must be object | `{ header: {
|
|
949
|
+
| Passed array to `[layout]` | Top-level must be object | `{ header: { menu: myArray } }` — see [Optional: API returns separate arrays](#optional-api-returns-separate-arrays-not-one-layout-object) |
|
|
972
950
|
| Auth always Sign in | `signedIn` not bound / false | Bind session; set `true` when logged in |
|
|
973
951
|
| Generic Manage Account | `userName` missing | Pass first name when `signedIn` is true |
|
|
974
952
|
| Layout changes ignored | In-place mutation | New object reference |
|
|
@@ -976,7 +954,7 @@ External `https://` links open in a new tab automatically.
|
|
|
976
954
|
| Tests fail | Elements undefined | `defineCustomElements()` in `beforeAll` |
|
|
977
955
|
| SSR `document is not defined` | Loader on server | `typeof window !== 'undefined'` guard |
|
|
978
956
|
| Missing social icons | Bad `platform` | Exactly `LinkedIn`, `Instagram`, `TikTok`, `YouTube` |
|
|
979
|
-
| Missing
|
|
957
|
+
| Missing menu item | Normalization | Non-empty `label` and valid `items` or `href` |
|
|
980
958
|
|
|
981
959
|
### DevTools
|
|
982
960
|
|
|
@@ -996,7 +974,7 @@ $0.userName
|
|
|
996
974
|
| --- | --- |
|
|
997
975
|
| `src/main.ts` | `defineCustomElements()` then bootstrap |
|
|
998
976
|
| `tsconfig.app.json` | `resolveJsonModule: true` |
|
|
999
|
-
| `src/app/auth.ts` | `HEADER_SIGN_IN`, `ACCOUNT_LOGIN_HREF
|
|
977
|
+
| `src/app/auth.ts` | `HEADER_SIGN_IN`, `ACCOUNT_LOGIN_HREF` |
|
|
1000
978
|
| `src/app/layout.types.ts` | `export type LayoutData = typeof layoutFixture` |
|
|
1001
979
|
| `src/app/layout.service.ts` | HTTP / API mapping (Step 7, optional arrays) |
|
|
1002
980
|
| `src/app/app.ts` | Shell: schema, layout, auth handler |
|
package/docs/angular.md
CHANGED
|
@@ -19,7 +19,7 @@ Reference demo: [weg-angular-demo](https://github.com/jobsac/weg-angular-demo).
|
|
|
19
19
|
| Tag | Purpose |
|
|
20
20
|
| --- | --- |
|
|
21
21
|
| `<weg-header>` | Header — CMS nav (signed out), built-in nav (signed in), auth |
|
|
22
|
-
| `<weg-footer>` | Footer — social,
|
|
22
|
+
| `<weg-footer>` | Footer — social, menu, credits, copyright |
|
|
23
23
|
|
|
24
24
|
Both accept the same `layout` payload ([`dummy-data.json`](../src/assets/dummy-data.json) or your API). `<weg-header>` also accepts **`signed-in`**, **`user-name`**, and emits **`wegAuthClick`**.
|
|
25
25
|
|
|
@@ -60,8 +60,6 @@ Add `schemas: [CUSTOM_ELEMENTS_SCHEMA]` to every `@Component` (or `@NgModule`) w
|
|
|
60
60
|
|
|
61
61
|
```ts
|
|
62
62
|
// src/app/auth.ts
|
|
63
|
-
export const HEADER_LOGO_HREF = 'https://www.warwickemploymentgroup.com/';
|
|
64
|
-
|
|
65
63
|
export const HEADER_SIGN_IN = {
|
|
66
64
|
label: 'Sign in',
|
|
67
65
|
href: 'https://account.warwickemploymentgroup.com/account/login',
|
|
@@ -89,7 +87,7 @@ import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
|
89
87
|
import { RouterOutlet } from '@angular/router';
|
|
90
88
|
import layoutFixture from 'weg-shared-layout/dummy-data.json';
|
|
91
89
|
|
|
92
|
-
import { ACCOUNT_LOGIN_HREF,
|
|
90
|
+
import { ACCOUNT_LOGIN_HREF, HEADER_SIGN_IN } from './auth';
|
|
93
91
|
|
|
94
92
|
@Component({
|
|
95
93
|
selector: 'app-root',
|
|
@@ -98,14 +96,7 @@ import { ACCOUNT_LOGIN_HREF, HEADER_LOGO_HREF, HEADER_SIGN_IN } from './auth';
|
|
|
98
96
|
templateUrl: './app.html',
|
|
99
97
|
})
|
|
100
98
|
export class App {
|
|
101
|
-
layoutData =
|
|
102
|
-
...layoutFixture,
|
|
103
|
-
header: {
|
|
104
|
-
...layoutFixture.header,
|
|
105
|
-
logoHref: HEADER_LOGO_HREF,
|
|
106
|
-
signIn: HEADER_SIGN_IN,
|
|
107
|
-
},
|
|
108
|
-
};
|
|
99
|
+
layoutData = layoutFixture;
|
|
109
100
|
|
|
110
101
|
signedIn = false;
|
|
111
102
|
userName?: string;
|
|
@@ -120,8 +111,7 @@ export class App {
|
|
|
120
111
|
return;
|
|
121
112
|
}
|
|
122
113
|
|
|
123
|
-
window.location.href =
|
|
124
|
-
this.layoutData.header?.signIn?.href ?? HEADER_SIGN_IN.href;
|
|
114
|
+
window.location.href = HEADER_SIGN_IN.href;
|
|
125
115
|
}
|
|
126
116
|
}
|
|
127
117
|
```
|
|
@@ -152,7 +142,7 @@ import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';
|
|
|
152
142
|
import { RouterOutlet } from '@angular/router';
|
|
153
143
|
import layoutFixture from 'weg-shared-layout/dummy-data.json';
|
|
154
144
|
|
|
155
|
-
import { ACCOUNT_LOGIN_HREF,
|
|
145
|
+
import { ACCOUNT_LOGIN_HREF, HEADER_SIGN_IN } from './auth';
|
|
156
146
|
import type { LayoutData } from './layout.types';
|
|
157
147
|
|
|
158
148
|
@Component({
|
|
@@ -162,14 +152,7 @@ import type { LayoutData } from './layout.types';
|
|
|
162
152
|
templateUrl: './app.html',
|
|
163
153
|
})
|
|
164
154
|
export class App {
|
|
165
|
-
readonly layoutData = signal<LayoutData>(
|
|
166
|
-
...layoutFixture,
|
|
167
|
-
header: {
|
|
168
|
-
...layoutFixture.header,
|
|
169
|
-
logoHref: HEADER_LOGO_HREF,
|
|
170
|
-
signIn: HEADER_SIGN_IN,
|
|
171
|
-
},
|
|
172
|
-
});
|
|
155
|
+
readonly layoutData = signal<LayoutData>(layoutFixture);
|
|
173
156
|
|
|
174
157
|
readonly signedIn = signal(false);
|
|
175
158
|
readonly userName = signal<string | undefined>(undefined);
|
|
@@ -185,8 +168,7 @@ export class App {
|
|
|
185
168
|
return;
|
|
186
169
|
}
|
|
187
170
|
|
|
188
|
-
window.location.href =
|
|
189
|
-
this.layoutData().header?.signIn?.href ?? HEADER_SIGN_IN.href;
|
|
171
|
+
window.location.href = HEADER_SIGN_IN.href;
|
|
190
172
|
}
|
|
191
173
|
}
|
|
192
174
|
```
|
|
@@ -211,7 +193,7 @@ HTTP with signals: **[Step 7B](./angular-integration-guide.md#step-7b--http-with
|
|
|
211
193
|
|
|
212
194
|
| Input / output | Binding (5A) | Binding (5B) | Notes |
|
|
213
195
|
| --- | --- | --- | --- |
|
|
214
|
-
| CMS nav | `[layout]="layoutData"` | `[layout]="layoutData()"` | `
|
|
196
|
+
| CMS nav | `[layout]="layoutData"` | `[layout]="layoutData()"` | `header.menu` — groups (`items`) and flat links (`href`) when signed out |
|
|
215
197
|
| Session state | `[signedIn]="signedIn"` | `[signedIn]="signedIn()"` | `true` → built-in signed-in nav |
|
|
216
198
|
| User name | `[userName]="userName"` | `[userName]="userName()"` | Manage Account label when signed in |
|
|
217
199
|
| Auth click | `(wegAuthClick)="onAuthClick($event)"` | `detail.action`: `'sign-in'` \| `'sign-out'` |
|
|
@@ -219,7 +201,7 @@ HTTP with signals: **[Step 7B](./angular-integration-guide.md#step-7b--http-with
|
|
|
219
201
|
|
|
220
202
|
**Signed-in nav (built in):** Find a job, Dashboard, Manage Account, Sign out.
|
|
221
203
|
|
|
222
|
-
Logo **image**
|
|
204
|
+
Logo **image** uses `layout.header.logoSrc` (bundled if omitted). Logo **link** always goes to WEG home.
|
|
223
205
|
|
|
224
206
|
## Footer
|
|
225
207
|
|
package/docs/nextjs.md
CHANGED
|
@@ -7,7 +7,7 @@ Guide for **Next.js 13+ App Router** (`app/` directory). For Vite/CRA-style clie
|
|
|
7
7
|
| Tag | Purpose |
|
|
8
8
|
| --- | --- |
|
|
9
9
|
| `<weg-header>` | Site header — bundled logo, CMS nav (signed out), built-in nav (signed in), Sign in / Manage Account / Sign out |
|
|
10
|
-
| `<weg-footer>` | Site footer — social,
|
|
10
|
+
| `<weg-footer>` | Site footer — social, menu, legal text |
|
|
11
11
|
|
|
12
12
|
Both accept **`layout`** (JSON string recommended in Next). `<weg-header>` also accepts **`signed-in`**, **`user-name`**, and emits **`wegAuthClick`**.
|
|
13
13
|
|
|
@@ -79,10 +79,12 @@ defineCustomElements();
|
|
|
79
79
|
|
|
80
80
|
type LayoutData = {
|
|
81
81
|
header?: {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
logoSrc?: string;
|
|
83
|
+
menu?: {
|
|
84
|
+
label: string;
|
|
85
|
+
href?: string;
|
|
86
|
+
items?: { label: string; href: string }[];
|
|
87
|
+
}[];
|
|
86
88
|
};
|
|
87
89
|
};
|
|
88
90
|
|
|
@@ -107,7 +109,7 @@ export function Header({
|
|
|
107
109
|
return;
|
|
108
110
|
}
|
|
109
111
|
|
|
110
|
-
window.location.href =
|
|
112
|
+
window.location.href = HEADER_SIGN_IN.href;
|
|
111
113
|
},
|
|
112
114
|
[layout, onSignedInChange],
|
|
113
115
|
);
|
|
@@ -234,9 +236,8 @@ Wire **`signedIn`** and **`userName`** from your auth provider (session hook / c
|
|
|
234
236
|
|
|
235
237
|
| API | Signed out | Signed in |
|
|
236
238
|
| --- | --- | --- |
|
|
237
|
-
| `layout.header.
|
|
238
|
-
| `layout.header.
|
|
239
|
-
| `layout.header.logoHref` | Logo link target | Built-in WEG home URL |
|
|
239
|
+
| `layout.header.menu` | CMS nav rendered (groups + flat links incl. Sign in) | Ignored — built-in nav used |
|
|
240
|
+
| `layout.header.logoSrc` | Logo image URL (bundled if omitted) | Built-in bundled logo |
|
|
240
241
|
| `signed-in` prop | `false` | `true` — session flag from host app |
|
|
241
242
|
| `user-name` prop | — | User's first name on Manage Account |
|
|
242
243
|
| `wegAuthClick` event | `'sign-in'` on Sign in click | `'sign-out'` on Sign out click |
|
|
@@ -244,7 +245,7 @@ Wire **`signedIn`** and **`userName`** from your auth provider (session hook / c
|
|
|
244
245
|
|
|
245
246
|
**Signed-in nav (built into the component):** Find a job, Dashboard, Manage Account, Sign out.
|
|
246
247
|
|
|
247
|
-
The logo **image**
|
|
248
|
+
The logo **image** uses `layout.header.logoSrc` when signed out (bundled if omitted). The logo **link** always goes to WEG home.
|
|
248
249
|
|
|
249
250
|
## Passing `layout` — quick reference
|
|
250
251
|
|
package/docs/react.md
CHANGED
|
@@ -7,7 +7,7 @@ Guide for **client-rendered** React apps (Vite, Create React App, etc.). If you
|
|
|
7
7
|
| Tag | Purpose |
|
|
8
8
|
| --- | --- |
|
|
9
9
|
| `<weg-header>` | Site header — bundled logo, CMS nav (signed out), built-in nav (signed in), Sign in / Manage Account / Sign out |
|
|
10
|
-
| `<weg-footer>` | Site footer — social links,
|
|
10
|
+
| `<weg-footer>` | Site footer — social links, menu, credits, copyright |
|
|
11
11
|
|
|
12
12
|
Both are **presentational** [Stencil](https://stenciljs.com/) Web Components. They **do not fetch data** — your app passes a **`layout`** payload (API, CMS, or [`dummy-data.json`](../src/assets/dummy-data.json)).
|
|
13
13
|
|
|
@@ -89,7 +89,7 @@ export const ACCOUNT_LOGIN_HREF = HEADER_SIGN_IN.href;
|
|
|
89
89
|
|
|
90
90
|
### Signed out
|
|
91
91
|
|
|
92
|
-
Pass CMS/API layout with
|
|
92
|
+
Pass CMS/API layout with unified `header.menu`. Use [`dummy-data.json`](../src/assets/dummy-data.json) for the full shape — dropdown groups use `items`, flat links and Sign in use `href`. Keep auth URLs in a local file:
|
|
93
93
|
|
|
94
94
|
```ts
|
|
95
95
|
// auth.ts (host app)
|
|
@@ -124,7 +124,7 @@ export function SiteHeader() {
|
|
|
124
124
|
return;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
window.location.href =
|
|
127
|
+
window.location.href = HEADER_SIGN_IN.href;
|
|
128
128
|
}, []);
|
|
129
129
|
|
|
130
130
|
return (
|
package/docs/vanilla.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
| Tag | Purpose |
|
|
6
6
|
| --- | --- |
|
|
7
7
|
| `<weg-header>` | Site header — bundled logo, CMS nav (signed out), built-in nav (signed in), Sign in / Manage Account / Sign out |
|
|
8
|
-
| `<weg-footer>` | Site footer — social links,
|
|
8
|
+
| `<weg-footer>` | Site footer — social links, menu, credits, copyright |
|
|
9
9
|
|
|
10
10
|
Both components are **presentational**: they do **not** fetch data. Pass the same `layout` payload to each.
|
|
11
11
|
|
|
@@ -53,17 +53,15 @@ element.layout = layoutObject;
|
|
|
53
53
|
Or pass a JSON **string** on the `layout` attribute:
|
|
54
54
|
|
|
55
55
|
```html
|
|
56
|
-
<weg-header layout='{"header":{"
|
|
56
|
+
<weg-header layout='{"header":{"menu":[]}}'></weg-header>
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
## Header layout fields (signed out)
|
|
60
60
|
|
|
61
61
|
| Field | Purpose |
|
|
62
62
|
| --- | --- |
|
|
63
|
-
| `header.
|
|
64
|
-
| `header.
|
|
65
|
-
| `header.links` | Flat nav links from CMS |
|
|
66
|
-
| `header.signIn` | Sign in button `{ label, href }` |
|
|
63
|
+
| `header.logoSrc` | Logo image URL (bundled if omitted) |
|
|
64
|
+
| `header.menu` | Unified nav — groups with `items[]`, flat links with `href` (incl. Sign in) |
|
|
67
65
|
|
|
68
66
|
See [`dummy-data.json`](../src/assets/dummy-data.json) for production URL examples.
|
|
69
67
|
|
|
@@ -82,7 +80,7 @@ export const ACCOUNT_LOGIN_HREF = HEADER_SIGN_IN.href;
|
|
|
82
80
|
|
|
83
81
|
### Signed out
|
|
84
82
|
|
|
85
|
-
Renders `
|
|
83
|
+
Renders `header.menu` from `layout` — dropdown groups, flat links, and the Sign in entry (label **Sign in** fires `wegAuthClick`).
|
|
86
84
|
|
|
87
85
|
### Signed in
|
|
88
86
|
|
|
@@ -110,16 +108,16 @@ header.addEventListener('wegAuthClick', (event) => {
|
|
|
110
108
|
return;
|
|
111
109
|
}
|
|
112
110
|
|
|
113
|
-
window.location.href =
|
|
111
|
+
window.location.href = HEADER_SIGN_IN.href;
|
|
114
112
|
});
|
|
115
113
|
```
|
|
116
114
|
|
|
117
115
|
| `event.detail.action` | Default behaviour if not prevented |
|
|
118
116
|
| --- | --- |
|
|
119
|
-
| `'sign-in'` | Browser follows `
|
|
117
|
+
| `'sign-in'` | Browser follows the Sign in menu entry's `href` |
|
|
120
118
|
| `'sign-out'` | Redirects to built-in sign-out URL |
|
|
121
119
|
|
|
122
|
-
Logo **image**
|
|
120
|
+
Logo **image** uses `header.logoSrc` (bundled if omitted). Logo **link** always goes to WEG home.
|
|
123
121
|
|
|
124
122
|
## Without a bundler
|
|
125
123
|
|