Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AppSideNav - Component branch (HDS-3800) #2384

Open
wants to merge 67 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
1466923
HDS-3800 Add AppSideNav component initial files
KristinLBradley Sep 3, 2024
77d3d42
HDS-3800 Fix failing tests
KristinLBradley Sep 3, 2024
ab67e2b
HDS-3800 Include app-side-nav Showcase styles, update Showcase to mat…
KristinLBradley Sep 4, 2024
a73392e
HDS-3800 Regenerate design tokens in output
KristinLBradley Sep 4, 2024
984c07e
HDS-3800 Update JS & CSS for Showcase display
KristinLBradley Sep 4, 2024
ab88bd8
HDS-3800 Make preliminary css updates to AppSideNav to replace dark t…
KristinLBradley Sep 4, 2024
f5ae7e7
HDS-3800 Add new framed example with AppSideNav
KristinLBradley Sep 4, 2024
8ab142b
HDS-3800 Update styles for current active List::Link
KristinLBradley Sep 5, 2024
404e049
HDS-3800 Update Badges in AppSideNav to outlined variant
KristinLBradley Sep 5, 2024
f1d5a1c
HDS-3800 Add border-radius to active indicator
KristinLBradley Sep 5, 2024
63bcd86
HDS-3800 Update JSON values
KristinLBradley Sep 5, 2024
a89a56d
HDS-3800 Regenerate design tokens in output
KristinLBradley Sep 5, 2024
c2e6e60
HDS-3800 Fix states for active List::Link, remove temporary token def…
KristinLBradley Sep 5, 2024
2e8e3f3
HDS-3800 Fix background color of :active state for List::Link
KristinLBradley Sep 5, 2024
3152559
HDS-3800 Expose color prop of icons in List::Link, add example with c…
KristinLBradley Sep 6, 2024
9c0a2bc
HDS-3800 Remove interaction styles for current/active link since it d…
KristinLBradley Sep 6, 2024
eb0ffe2
HDS-3800 Add fix for toggle border overlaying AppSideNav border
KristinLBradley Sep 6, 2024
579407f
HDS-3800 Add clip-path to prevent white background of toggle corners …
KristinLBradley Sep 6, 2024
6909241
HDS-3800 Add test for iconColor arg
KristinLBradley Sep 10, 2024
29a94e1
HDS-3800 Remove NavigatorNarrator (a11y-refocus) from AppSideNav
KristinLBradley Sep 10, 2024
631e392
HDS-3800 Remove old argument, fix failing test
KristinLBradley Sep 10, 2024
daca85c
HDS-3800 Fix failing tests, comment out web tests until web docs are …
KristinLBradley Sep 11, 2024
e64c812
HDS-3800 Remove skip link example from Showcase as it's no longer app…
KristinLBradley Sep 11, 2024
aeccc1f
HDS-3800 Undo temp styles for testing skip link
KristinLBradley Sep 11, 2024
75e0651
HDS-3800 Remove header component and HomeLink child component
KristinLBradley Sep 12, 2024
f7743ca
HDS-3800 Clean up Showcase examples
KristinLBradley Sep 12, 2024
44e2ea4
HDS-3800 Update to remove ember-testing reference matching SideNav co…
KristinLBradley Sep 12, 2024
6e300b7
HDS-3800 Clean up type definitions
KristinLBradley Sep 18, 2024
40192d0
HDS-3800 Rename ariaLabel for toggle button for clarity
KristinLBradley Sep 18, 2024
3cdbfb3
HDS-3800 Add aria-current=page for active ListLink
KristinLBradley Sep 18, 2024
009d337
HDS-3800 Delete TODO comment
KristinLBradley Sep 18, 2024
282bd77
HDS-3800 Remove examples of nested BUttons & Dropdowns from Showcase …
KristinLBradley Sep 18, 2024
3b71598
HDS-3800 Set negative tabindex for active Link
KristinLBradley Sep 18, 2024
3fe5d5a
HDS-3800 Remove background-color from hds-app-side-nav__list-item-link
KristinLBradley Sep 18, 2024
727a0e3
HDS-3800 Refactor .active state styles to fix issue with link :active…
KristinLBradley Sep 19, 2024
c6d77f7
HDS-3800 Improve toggle tab rounded corner rendering
KristinLBradley Sep 19, 2024
21fe111
HDS-3800 Remove colored icons from framed example, remove typesafe di…
KristinLBradley Sep 19, 2024
40ece7e
HDS-3800 Update color used for border in design tokens, update relate…
KristinLBradley Sep 20, 2024
e6dc784
HDS-3800 Regenerate design tokens in output
KristinLBradley Sep 20, 2024
c725d27
HDS-3800 Remove negative tabIndex
KristinLBradley Sep 20, 2024
9ac02f2
HDS-3800 Add example of BackLink used in context to Showcase
KristinLBradley Sep 20, 2024
d9c7e7b
HDS-3800 Remove uneeded id
KristinLBradley Sep 20, 2024
6876c88
HDS-3800 Port fix for inert synchronization from SideNav
KristinLBradley Sep 20, 2024
4e7c0a3
HDS-3800 Update design token value used by titles
KristinLBradley Sep 20, 2024
51560a9
HDS-3800 Regenerate design tokens in output
KristinLBradley Sep 20, 2024
99e4945
HDS-3800 UPdate list item color
KristinLBradley Sep 20, 2024
c1f03d9
HDS-3800 Add changelog
KristinLBradley Sep 23, 2024
dcfd306
Update silver-planes-occur.md
KristinLBradley Sep 23, 2024
d939aa3
Update silver-planes-occur.md
KristinLBradley Sep 24, 2024
4f1f8e0
HDS-3800 Remove glint-expect-error
KristinLBradley Sep 25, 2024
d567feb
HDS-3800 Update to swap empty component classes to templateOnly and m…
KristinLBradley Sep 25, 2024
c0c937a
HDS-3800 Remove Component from names to match SideNav updates
KristinLBradley Sep 25, 2024
deebe82
HDS-3800 Fix incorrect class names
KristinLBradley Sep 25, 2024
76b8ad4
HDS-3800 Update comment
KristinLBradley Sep 25, 2024
83c8e40
HDS-3800 Remove iconColor option
KristinLBradley Sep 27, 2024
729b68d
`AppSideNav` - overlay scrolling bug fix (HDS-3914) (#2472)
KristinLBradley Oct 2, 2024
a6a8164
`AppSideNav` - Follow-on improvements for headers & aria (HDS-3897) (…
KristinLBradley Oct 3, 2024
d1e1b8e
`AppSideNav` - Web docs (HDS-3807) (#2410)
KristinLBradley Oct 4, 2024
29113ab
`AppSideNav` - Remove named blocks (HDS-3924) (#2477)
KristinLBradley Oct 8, 2024
4a4ddad
`AppSideNav` - Replace tracked variables from args (#2486)
KristinLBradley Oct 8, 2024
b7637e7
`AppSideNav` - Fix bug with scrolling caused by hidden panels (HDS-38…
KristinLBradley Oct 15, 2024
7dbca3a
`SideNav` - Deprecate component (HDS-3848) (#2425)
KristinLBradley Oct 15, 2024
974d7f0
Update website/docs/components/app-side-nav/index.md
KristinLBradley Oct 16, 2024
f5aae84
HDS-3800 Update references to versioning
KristinLBradley Oct 16, 2024
44aa1f8
Merge branch 'main' into hds-3800-app-sidenav-component
KristinLBradley Oct 16, 2024
aa9198b
HDS-3800 Add back missing line
KristinLBradley Oct 16, 2024
7d1baae
HDS-3800 export signature
KristinLBradley Oct 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/seven-mugs-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hashicorp/design-system-components": minor
---

`SideNav` - Deprecated the `SideNav` component. Use the [`AppSideNav` component](/components/app-side-nav) as a replacement.
5 changes: 5 additions & 0 deletions .changeset/silver-planes-occur.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hashicorp/design-system-components": minor
---

`Hds::AppSideNav` - Added the component. It is meant to replace the standalone `SideNav` component and be paired together with the `AppHeader` which should both be contained within the `AppFrame` layout component.
9 changes: 9 additions & 0 deletions packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,15 @@
"./components/hds/app-header/home-link.js": "./dist/_app_/components/hds/app-header/home-link.js",
"./components/hds/app-header/index.js": "./dist/_app_/components/hds/app-header/index.js",
"./components/hds/app-header/menu-button.js": "./dist/_app_/components/hds/app-header/menu-button.js",
"./components/hds/app-side-nav/index.js": "./dist/_app_/components/hds/app-side-nav/index.js",
"./components/hds/app-side-nav/list/back-link.js": "./dist/_app_/components/hds/app-side-nav/list/back-link.js",
"./components/hds/app-side-nav/list/index.js": "./dist/_app_/components/hds/app-side-nav/list/index.js",
"./components/hds/app-side-nav/list/item.js": "./dist/_app_/components/hds/app-side-nav/list/item.js",
"./components/hds/app-side-nav/list/link.js": "./dist/_app_/components/hds/app-side-nav/list/link.js",
"./components/hds/app-side-nav/list/title.js": "./dist/_app_/components/hds/app-side-nav/list/title.js",
"./components/hds/app-side-nav/portal/index.js": "./dist/_app_/components/hds/app-side-nav/portal/index.js",
"./components/hds/app-side-nav/portal/target.js": "./dist/_app_/components/hds/app-side-nav/portal/target.js",
"./components/hds/app-side-nav/toggle-button.js": "./dist/_app_/components/hds/app-side-nav/toggle-button.js",
"./components/hds/application-state/body.js": "./dist/_app_/components/hds/application-state/body.js",
"./components/hds/application-state/footer.js": "./dist/_app_/components/hds/application-state/footer.js",
"./components/hds/application-state/header.js": "./dist/_app_/components/hds/application-state/header.js",
Expand Down
34 changes: 34 additions & 0 deletions packages/components/src/components/hds/app-side-nav/index.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
}}
{{! IMPORTANT: we need to add "squishies" here (~) because otherwise the whitespace added by Ember causes the empty element to still have visible padding - See https://handlebarsjs.com/guide/expressions.html#whitespace-control }}
<div
class={{this.classNames}}
...attributes
{{on "transitionstart" (fn this.setTransition "start")}}
{{on "transitionend" (fn this.setTransition "end")}}
{{! @glint-expect-error - https://github.com/josemarluedke/ember-focus-trap/issues/86 }}
{{focus-trap isActive=this.shouldTrapFocus}}
{{did-insert this.didInsert}}
>
<h2 class="sr-only" id="hds-app-side-nav-header">Application local navigation</h2>

<div class="hds-app-side-nav__wrapper">
{{#if this.showToggleButton}}
{{! template-lint-disable no-invalid-interactive}}
<div class="hds-app-side-nav__overlay" {{on "click" this.toggleMinimizedStatus}} />
{{! template-lint-enable no-invalid-interactive}}
<Hds::AppSideNav::ToggleButton
aria-labelledby="hds-app-side-nav-header"
aria-expanded={{if this.isMinimized "false" "true"}}
@icon={{if this.isMinimized "chevrons-right" "chevrons-left"}}
{{on "click" this.toggleMinimizedStatus}}
/>
{{/if}}

<div class="hds-app-side-nav__wrapper-body">
{{~yield~}}
</div>
</div>
</div>
210 changes: 210 additions & 0 deletions packages/components/src/components/hds/app-side-nav/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
/**
KristinLBradley marked this conversation as resolved.
Show resolved Hide resolved
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/

import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { registerDestructor } from '@ember/destroyable';

export interface HdsAppSideNavSignature {
Args: {
isResponsive?: boolean;
isCollapsible?: boolean;
isMinimized?: boolean;
onToggleMinimizedStatus?: (arg: boolean) => void;
onDesktopViewportChange?: (arg: boolean) => void;
};
Blocks: {
default?: [];
};
Element: HTMLDivElement;
}

export default class HdsAppSideNav extends Component<HdsAppSideNavSignature> {
@tracked isMinimized;
@tracked isAnimating = false;
@tracked isDesktop = true;

body!: HTMLElement;
bodyInitialOverflowValue = '';
desktopMQ: MediaQueryList;
containersToHide!: NodeListOf<Element>;

desktopMQVal = getComputedStyle(document.documentElement).getPropertyValue(
'--hds-app-desktop-breakpoint'
);

constructor(owner: unknown, args: HdsAppSideNavSignature['Args']) {
super(owner, args);
this.isMinimized = this.args.isMinimized ?? false; // sets the default state on 'desktop' viewports
this.desktopMQ = window.matchMedia(`(min-width:${this.desktopMQVal})`);
this.addEventListeners();
registerDestructor(this, (): void => {
this.removeEventListeners();
});
}

addEventListeners(): void {
document.addEventListener('keydown', this.escapePress, true);
this.desktopMQ.addEventListener('change', this.updateDesktopVariable, true);
shleewhite marked this conversation as resolved.
Show resolved Hide resolved
// if not instantiated as minimized via arguments
if (!this.args.isMinimized) {
// set initial state based on viewport using a "synthetic" event
const syntheticEvent = new MediaQueryListEvent('change', {
matches: this.desktopMQ.matches,
media: this.desktopMQ.media,
});
this.updateDesktopVariable(syntheticEvent);
}
}

removeEventListeners(): void {
document.removeEventListener('keydown', this.escapePress, true);
this.desktopMQ.removeEventListener(
'change',
this.updateDesktopVariable,
true
);
}

// controls if the component reacts to viewport changes
get isResponsive(): boolean {
return this.args.isResponsive ?? true;
}

// controls if users can collapse the appsidenav on 'desktop' viewports
get isCollapsible(): boolean {
return this.args.isCollapsible ?? false;
}

get shouldTrapFocus(): boolean {
return this.isResponsive && !this.isDesktop && !this.isMinimized;
}

get showToggleButton(): boolean {
return (this.isResponsive && !this.isDesktop) || this.isCollapsible;
}

get classNames(): string {
const classes = [`hds-app-side-nav`];

// add specific class names for the different possible states
if (this.isResponsive) {
classes.push('hds-app-side-nav--is-responsive');
}
if (!this.isDesktop && this.isResponsive) {
classes.push('hds-app-side-nav--is-mobile');
} else {
classes.push('hds-app-side-nav--is-desktop');
}
if (this.isMinimized && this.isResponsive) {
classes.push('hds-app-side-nav--is-minimized');
} else {
classes.push('hds-app-side-nav--is-not-minimized');
}
if (this.isAnimating) {
classes.push('hds-app-side-nav--is-animating');
}

return classes.join(' ');
}

synchronizeInert(): void {
this.containersToHide?.forEach((element): void => {
if (this.isMinimized) {
element.setAttribute('inert', '');
} else {
element.removeAttribute('inert');
}
});
}

lockBodyScroll(): void {
if (this.body) {
// Prevent page from scrolling when the dialog is open
this.body.style.setProperty('overflow', 'hidden');
}
}

unlockBodyScroll(): void {
// Reset page `overflow` property
if (this.body) {
this.body.style.removeProperty('overflow');
if (this.bodyInitialOverflowValue === '') {
if (this.body.style.length === 0) {
this.body.removeAttribute('style');
}
} else {
this.body.style.setProperty('overflow', this.bodyInitialOverflowValue);
}
}
}

@action
escapePress(event: KeyboardEvent): void {
if (event.key === 'Escape' && !this.isMinimized && !this.isDesktop) {
this.isMinimized = true;
this.synchronizeInert();
}
}

@action
toggleMinimizedStatus(): void {
this.isMinimized = !this.isMinimized;
this.synchronizeInert();

const { onToggleMinimizedStatus } = this.args;

if (typeof onToggleMinimizedStatus === 'function') {
onToggleMinimizedStatus(this.isMinimized);
}

if (this.isMinimized) {
this.unlockBodyScroll();
} else {
this.lockBodyScroll();
}
}

@action
didInsert(element: HTMLElement): void {
this.containersToHide = element.querySelectorAll(
'.hds-app-side-nav-hide-when-minimized'
);
this.body = document.body;
// Store the initial `overflow` value of `<body>` so we can reset to it
this.bodyInitialOverflowValue =
this.body.style.getPropertyValue('overflow');
}

@action
setTransition(phase: string, event: TransitionEvent): void {
// we only want to respond to `width` animation/transitions
if (event.propertyName !== 'width') {
return;
}
if (phase === 'start') {
this.isAnimating = true;
} else {
this.isAnimating = false;
}
}

@action
updateDesktopVariable(event: MediaQueryListEvent): void {
this.isDesktop = event.matches;

// automatically minimize on narrow viewports (when not in desktop mode)
this.isMinimized = !this.isDesktop;

this.synchronizeInert();

const { onDesktopViewportChange } = this.args;

if (typeof onDesktopViewportChange === 'function') {
onDesktopViewportChange(this.isDesktop);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
}}

<Hds::AppSideNav::List::Item>
<Hds::Interactive
class="hds-app-side-nav__list-item-link hds-app-side-nav__list-item-link--back-link"
@current-when={{@current-when}}
@models={{hds-link-to-models @model @models}}
@query={{hds-link-to-query @query}}
@replace={{@replace}}
@route={{@route}}
@isRouteExternal={{@isRouteExternal}}
@href={{@href}}
@isHrefExternal={{@isHrefExternal}}
...attributes
>
<Hds::Icon class="hds-app-side-nav__list-item-icon-leading" @name="chevron-left" />
<span class="hds-app-side-nav__list-item-text hds-typography-body-200 hds-font-weight-medium">
{{@text}}
</span>
</Hds::Interactive>
</Hds::AppSideNav::List::Item>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/

import TemplateOnlyComponent from '@ember/component/template-only';

import type { HdsInteractiveSignature } from '../../interactive';

export interface HdsAppSideNavListBackLinkSignature {
Args: HdsInteractiveSignature['Args'] & {
text: string;
};
Element: HdsInteractiveSignature['Element'];
}

const HdsAppSideNavListBackLink =
TemplateOnlyComponent<HdsAppSideNavListBackLinkSignature>();

export default HdsAppSideNavListBackLink;
19 changes: 19 additions & 0 deletions packages/components/src/components/hds/app-side-nav/list/index.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
}}

<nav class="hds-app-side-nav__list-wrapper" aria-labelledby="hds-app-side-nav-header" ...attributes>
{{yield (hash ExtraBefore=(component "hds/yield"))}}
<ul class="hds-app-side-nav__list" role="list" aria-labelledby={{this.titleIds}}>
{{yield
(hash
Item=(component "hds/app-side-nav/list/item")
BackLink=(component "hds/app-side-nav/list/back-link")
Title=(component "hds/app-side-nav/list/title" didInsertTitle=this.didInsertTitle)
Link=(component "hds/app-side-nav/list/link")
)
}}
</ul>
{{yield (hash ExtraAfter=(component "hds/yield"))}}
</nav>
43 changes: 43 additions & 0 deletions packages/components/src/components/hds/app-side-nav/list/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/

import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import type { ComponentLike } from '@glint/template';
import type { HdsYieldSignature } from '../../yield';
import type { HdsAppSideNavListItemSignature } from './item';
import type { HdsAppSideNavListBackLinkSignature } from './back-link';
import type { HdsAppSideNavListTitleSignature } from './title';
import type { HdsAppSideNavListLinkSignature } from './link';

export interface HdsAppSideNavListSignature {
Blocks: {
default: [
{
ExtraBefore?: ComponentLike<HdsYieldSignature>;
Item?: ComponentLike<HdsAppSideNavListItemSignature>;
BackLink?: ComponentLike<HdsAppSideNavListBackLinkSignature>;
Title?: ComponentLike<HdsAppSideNavListTitleSignature>;
Link?: ComponentLike<HdsAppSideNavListLinkSignature>;
ExtraAfter?: ComponentLike<HdsYieldSignature>;
},
];
};
Element: HTMLElement;
}

export default class HdsAppSideNavList extends Component<HdsAppSideNavListSignature> {
@tracked _titleIds: string[] = [];

get titleIds(): string {
return this._titleIds.join(' ');
}

@action
didInsertTitle(titleId: string): void {
this._titleIds = [...this._titleIds, titleId];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
}}

<li class="hds-app-side-nav__list-item" ...attributes>
{{yield}}
</li>
Loading