Skip to content

Commit 5a3a79b

Browse files
AppHeader: “Enterprise Nav” phase 1.5 updates (HDS-3613) (#2306)
1 parent 5b57327 commit 5a3a79b

File tree

12 files changed

+205
-48
lines changed

12 files changed

+205
-48
lines changed

.changeset/fuzzy-melons-dream.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@hashicorp/design-system-components": minor
3+
---
4+
5+
`AppHeader`:
6+
- Hide the closed menu content in mobile view using CSS instead of conditionally rendering/not rendering the menu content.
7+
- Add `NavigationNarrator` with associated arguments to provide a "skip link".

packages/components/src/components/hds/app-header/index.hbs

+18-10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@
44
}}
55

66
<div class={{this.classNames}} ...attributes>
7+
{{#if (and this.hasA11yRefocus (not this.isOpen))}}
8+
{{! @glint-expect-error - `ember-a11y-refocus` doesn't expose types yet }}
9+
<NavigationNarrator
10+
@routeChangeValidator={{@a11yRefocusRouteChangeValidator}}
11+
@skipTo={{this.a11yRefocusSkipTo}}
12+
@skipText={{@a11yRefocusSkipText}}
13+
@navigationText={{@a11yRefocusNavigationText}}
14+
@excludeAllQueryParams={{@a11yRefocusExcludeAllQueryParams}}
15+
/>
16+
{{/if}}
17+
718
{{yield to="logo"}}
819

920
{{#if (not this.isDesktop)}}
@@ -13,17 +24,14 @@
1324
@menuContentId={{this.menuContentId}}
1425
/>
1526
{{/if}}
27+
1628
<div class="hds-app-header__actions" id={{this.menuContentId}}>
17-
{{#if this.showItems}}
18-
<div class="hds-app-header__actions-content">
19-
<div class="hds-app-header__global-actions">
20-
{{yield to="globalActions"}}
21-
</div>
29+
<div class="hds-app-header__global-actions">
30+
{{yield to="globalActions"}}
31+
</div>
2232

23-
<div class="hds-app-header__utility-actions">
24-
{{yield to="utilityActions"}}
25-
</div>
26-
</div>
27-
{{/if}}
33+
<div class="hds-app-header__utility-actions">
34+
{{yield to="utilityActions"}}
35+
</div>
2836
</div>
2937
</div>

packages/components/src/components/hds/app-header/index.ts

+23-7
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ import { registerDestructor } from '@ember/destroyable';
1212
export interface HdsAppHeaderSignature {
1313
Args: {
1414
breakpoint?: string;
15+
hasA11yRefocus?: boolean;
16+
a11yRefocusSkipTo?: string;
17+
a11yRefocusSkipText?: string;
18+
a11yRefocusNavigationText?: string;
19+
a11yRefocusRouteChangeValidator?: string;
20+
a11yRefocusExcludeAllQueryParams?: boolean;
1521
};
1622
Blocks: {
1723
logo?: [];
@@ -24,11 +30,20 @@ export interface HdsAppHeaderSignature {
2430
export default class HdsAppHeaderComponent extends Component<HdsAppHeaderSignature> {
2531
@tracked isOpen = false;
2632
@tracked isDesktop = true;
33+
@tracked hasOverflowContent = false;
2734
desktopMQ: MediaQueryList;
35+
hasA11yRefocus = this.args.hasA11yRefocus ?? true;
36+
a11yRefocusSkipTo = '#' + (this.args.a11yRefocusSkipTo ?? 'main');
2837

2938
// Generates a unique ID for the Menu Content
3039
menuContentId = 'hds-menu-content-' + guidFor(this);
3140

41+
breakpoint = parseInt(
42+
getComputedStyle(document.documentElement).getPropertyValue(
43+
'--hds-app-desktop-breakpoint'
44+
)
45+
);
46+
3247
desktopMQVal =
3348
this.args.breakpoint ??
3449
getComputedStyle(document.documentElement).getPropertyValue(
@@ -65,19 +80,20 @@ export default class HdsAppHeaderComponent extends Component<HdsAppHeaderSignatu
6580
);
6681
}
6782

68-
// Menu items display if is desktop, or if is mobile and the menu is open
69-
get showItems(): boolean {
70-
return this.isDesktop || this.isOpen;
71-
}
72-
7383
// Get the class names to apply to the component.
7484
get classNames(): string {
7585
const classes = ['hds-app-header'];
7686

77-
if (!this.isDesktop) {
87+
if (this.isDesktop) {
88+
classes.push('hds-app-header--is-desktop');
89+
} else {
7890
classes.push('hds-app-header--is-mobile');
91+
}
92+
93+
if (this.isOpen) {
94+
classes.push('hds-app-header--menu-is-open');
7995
} else {
80-
classes.push('hds-app-header--is-desktop');
96+
classes.push('hds-app-header--menu-is-closed');
8197
}
8298

8399
return classes.join(' ');

packages/components/src/styles/components/app-frame.scss

+6
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@
5353

5454
.hds-app-frame__main {
5555
grid-area: main;
56+
57+
// This prevents content from being hidden behind the fixed app-header when the skip link is used
58+
.hds-app-frame--has-header-with-sidebar & {
59+
margin-top: calc(var(--token-app-header-height) * -1);
60+
padding-top: var(--token-app-header-height);
61+
}
5662
}
5763

5864
.hds-app-frame__footer {

packages/components/src/styles/components/app-header.scss

+41-4
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,39 @@
2323
background: var(--token-color-palette-neutral-700);
2424
border-bottom: 1px solid var(--token-color-palette-neutral-500);
2525

26+
// A11Y-REFOCUS "SKIP LINK"
27+
28+
.ember-a11y-refocus-skip-link {
29+
top: 10px;
30+
left: 10px;
31+
z-index: 20;
32+
width: max-content;
33+
padding: 2px 10px 4px;
34+
color: var(--token-color-foreground-action);
35+
font-size: var(--token-typography-display-200-font-size);
36+
font-family: var(--token-typography-display-200-font-family);
37+
line-height: var(--token-typography-display-200-line-height);
38+
background-color: var(--token-color-surface-faint);
39+
border-radius: 3px;
40+
transform: translateY(-200%);
41+
transition: 0.6s ease-in-out;
42+
43+
&:focus {
44+
transform: translateY(0);
45+
}
46+
}
47+
2648
// RESPONSIVE VIEWS
2749

28-
// Desktop/large view:
50+
// Desktop (large) view:
2951
&.hds-app-header--is-desktop {
3052
// Global actions are aligned next to the logo in desktop view
3153
.hds-app-header__global-actions {
3254
margin-right: auto;
3355
}
3456
}
3557

36-
// Mobile/small & Tablet/medium compound view:
58+
// Mobile/tablet (small/medium) view:
3759
&.hds-app-header--is-mobile {
3860
// Actions content appears as a dropdown menu in mobile/tablet view
3961
.hds-app-header__actions {
@@ -43,15 +65,30 @@
4365
left: 0;
4466
}
4567

46-
.hds-app-header__actions-content {
68+
// Display as dropdown menu in mobile/tablet view
69+
.hds-app-header__actions {
4770
flex-wrap: wrap;
4871
align-content: flex-start;
49-
height: calc(100vh - var(--token-app-header-height));
5072
padding: 16px;
5173
overflow: auto; // allow users to scroll if the content is too long
5274
background: var(--token-color-palette-neutral-700);
5375
}
5476

77+
&.hds-app-header--menu-is-closed {
78+
.hds-app-header__actions {
79+
height: 0;
80+
padding-top: 0;
81+
padding-bottom: 0;
82+
}
83+
}
84+
85+
&.hds-app-header--menu-is-open {
86+
.hds-app-header__actions {
87+
height: calc(100vh - var(--token-app-header-height));
88+
}
89+
}
90+
91+
5592
// Force the width of global actions to 100% in mobile/tablet view
5693
.hds-app-header__global-actions {
5794
&,

showcase/app/styles/showcase-pages/app-frame.scss

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ body.layouts-app-frame {
1818
.shw-component-app-frame-container {
1919
position: relative;
2020
min-height: 0;
21+
overflow: hidden; // hide the skip link which is positioned outside the frame
2122
border: 1px solid;
2223
box-shadow: 0 2px 4px rgba(0, 0, 0, 30%);
2324

showcase/app/templates/components/app-header.hbs

+7-7
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
<Shw::Grid @columns={{1}} {{style gap="2rem"}} as |SG|>
1616
<SG.Item>
17-
<Hds::AppHeader>
17+
<Hds::AppHeader @hasA11yRefocus={{false}}>
1818
<:logo>
1919
<Shw::Placeholder @height="2em" @width="auto" @text="Logo" />
2020
</:logo>
@@ -39,7 +39,7 @@
3939

4040
<Shw::Grid @columns={{1}} {{style gap="3rem"}} as |SG|>
4141
<SG.Item @label="With minimum recommended phase 1 content">
42-
<Hds::AppHeader>
42+
<Hds::AppHeader @hasA11yRefocus={{false}}>
4343
<:logo>
4444
<Hds::AppHeader::HomeLink @icon="hashicorp" @ariaLabel="HashiCorp home menu" @href="#" />
4545
</:logo>
@@ -76,7 +76,7 @@
7676
</SG.Item>
7777

7878
<SG.Item @label="With max recommended phase 1 content & Terraform logo">
79-
<Hds::AppHeader>
79+
<Hds::AppHeader @hasA11yRefocus={{false}}>
8080
<:logo>
8181
<Hds::AppHeader::HomeLink @icon="terraform" @ariaLabel="Terraform home menu" @href="#" />
8282
</:logo>
@@ -115,7 +115,7 @@
115115
</SG.Item>
116116

117117
<SG.Item @label="With max proposed future content">
118-
<Hds::AppHeader>
118+
<Hds::AppHeader @hasA11yRefocus={{false}}>
119119
<:logo>
120120
<Hds::AppHeader::HomeLink @icon="hashicorp" @ariaLabel="HashiCorp home menu" @href="#" />
121121
</:logo>
@@ -167,7 +167,7 @@
167167

168168
<Shw::Grid @columns={{1}} {{style gap="3rem"}} as |SG|>
169169
<SG.Item @label="40 characters long organization name with dashes">
170-
<Hds::AppHeader>
170+
<Hds::AppHeader @hasA11yRefocus={{false}}>
171171
<:logo>
172172
<Hds::AppHeader::HomeLink @icon="hashicorp" @ariaLabel="HashiCorp home menu" @href="#" />
173173
</:logo>
@@ -203,7 +203,7 @@
203203
</SG.Item>
204204

205205
<SG.Item @label="40 characters long organization name with no dashes">
206-
<Hds::AppHeader>
206+
<Hds::AppHeader @hasA11yRefocus={{false}}>
207207
<:logo>
208208
<Hds::AppHeader::HomeLink @icon="hashicorp" @ariaLabel="HashiCorp home menu" @href="#" />
209209
</:logo>
@@ -245,7 +245,7 @@
245245

246246
<Shw::Grid @columns={{1}} {{style gap="3rem"}} as |SG|>
247247
<SG.Item @label="With custom breakpoint (menu button is visible at wide screen width)">
248-
<Hds::AppHeader @breakpoint="20000px">
248+
<Hds::AppHeader @hasA11yRefocus={{false}} @breakpoint="20000px">
249249
<:logo>
250250
<Hds::AppHeader::HomeLink @icon="hashicorp" @ariaLabel="HashiCorp home menu" @href="#" />
251251
</:logo>

showcase/app/templates/layouts/app-frame/frameless/demo-full-app-frame-with-app-header.hbs

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
</Hds::SideNav>
8080
</Frame.Sidebar>
8181

82-
<Frame.Main>
82+
<Frame.Main id="main">
8383
<div class="shw-layout-app-frame__main-content-padding">
8484
<Hds::PageHeader as |PH|>
8585
<PH.Title>Page title</PH.Title>

showcase/app/templates/layouts/app-frame/frameless/demo-full-app-frame-with-side-nav.hbs

+8-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@
88

99
<Hds::AppFrame @hasHeader={{false}} as |Frame|>
1010
<Frame.Sidebar>
11-
<Hds::SideNav @isResponsive={{true}} @hasA11yRefocus={{false}} @isCollapsible={{true}} @hasHeader={{false}}>
11+
<Hds::SideNav
12+
@isResponsive={{true}}
13+
@a11yRefocusSkipTo="main"
14+
@hasA11yRefocus={{true}}
15+
@isCollapsible={{true}}
16+
@hasHeader={{false}}
17+
>
1218
<:header>
1319
<Hds::SideNav::Header>
1420
<:logo>
@@ -71,7 +77,7 @@
7177
</Hds::SideNav>
7278
</Frame.Sidebar>
7379

74-
<Frame.Main>
80+
<Frame.Main id="main">
7581
<div class="shw-layout-app-frame__main-content-padding">
7682
<Hds::PageHeader as |PH|>
7783
<PH.Title>Page title</PH.Title>

0 commit comments

Comments
 (0)