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

feat(elements|ino-nav-drawer): implement mobile nav-drawer #1035

Merged
merged 11 commits into from
Sep 28, 2023
4 changes: 2 additions & 2 deletions packages/elements-angular/src/directives/proxies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -712,13 +712,13 @@ export declare interface InoNavDrawer extends Components.InoNavDrawer {

@ProxyCmp({
defineCustomElementFn: undefined,
inputs: ['a11yLabels', 'anchor', 'open', 'variant']
inputs: ['a11yLabels', 'anchor', 'isMobile', 'open', 'variant']
})
@Component({
selector: 'ino-nav-drawer',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
inputs: ['a11yLabels', 'anchor', 'open', 'variant']
inputs: ['a11yLabels', 'anchor', 'isMobile', 'open', 'variant']
})
export class InoNavDrawer {
protected el: HTMLElement;
Expand Down
1 change: 1 addition & 0 deletions packages/elements-vue/src/proxies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ export const InoNavDrawer = /*@__PURE__*/ defineContainer<JSX.InoNavDrawer>('ino
'anchor',
'variant',
'a11yLabels',
'isMobile',
'openChange'
]);

Expand Down
8 changes: 8 additions & 0 deletions packages/elements/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,10 @@ export namespace Components {
* Side from which the drawer will appear. Possible values: `left` (default), `right`.
*/
"anchor"?: NavDrawerAnchor;
/**
* If true, the mobile drawer will be shown. Automatically sets the variant to `modal`.
*/
"isMobile"?: boolean;
/**
* Marks this element as open. (**unmanaged**)
*/
Expand Down Expand Up @@ -2703,6 +2707,10 @@ declare namespace LocalJSX {
* Side from which the drawer will appear. Possible values: `left` (default), `right`.
*/
"anchor"?: NavDrawerAnchor;
/**
* If true, the mobile drawer will be shown. Automatically sets the variant to `modal`.
*/
"isMobile"?: boolean;
/**
* Emits when the user clicks on the drawer toggle icon to change the open state. Contains the status in `event.detail`.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
@use '../base/mdc-customize';
@use '@material/drawer/mdc-drawer';
@use "@material/drawer";
@use "@material/list";
@use '../base/theme';
@use '@material/drawer';
@use '@material/list';
@use '../base/theme' as theme;
@use '../base/new-theme' as new-theme;

@include drawer.core-styles;
@include drawer.dismissible-core-styles;
Expand All @@ -13,6 +14,7 @@
/**
* @prop --ino-nav-drawer-width-open: The width of the open drawer.
* @prop --ino-nav-drawer-width-closed: Docked variant only! The width of the collapsed drawer.
* @prop --ino-nav-drawer-mobile-width-open: Mobile variant only! The width of the open drawer.
* @prop --ino-nav-drawer-background: Background of the drawer.
* @prop --ino-nav-drawer-height: Height of the drawer.
* @prop --ino-nav-drawer-text-color: Color of text inside the drawer.
Expand All @@ -22,12 +24,16 @@

--nav-drawer-width-open: var(--ino-nav-drawer-width-open, 250px);
--nav-drawer-width-closed: var(--ino-nav-drawer-width-closed, 72px);
--nav-drawer-mobile-width-open: var(
--ino-nav-drawer-mobile-width-open,
171px
);
--nav-drawer-height: var(--ino-nav-drawer-height, 100%);
--nav-drawer-background: var(
--ino-nav-drawer-background,
#{theme.color(primary, contrast)}
);
--nav-drawer-text-color: var(--ino-nav-drawer-text-color, theme.$brand);
--nav-drawer-text-color: var(--ino-nav-drawer-text-color, #{new-theme.$p-7});
--nav-drawer-transition-duration: var(
--ino-nav-drawer-transition-duration,
0.25s
Expand Down Expand Up @@ -117,6 +123,7 @@
justify-content: center;
align-items: center;
text-align: center;
white-space: normal;

.mdc-drawer__subtitle {
font-size: 12px;
Expand Down Expand Up @@ -223,4 +230,40 @@
top: 0;
}
}

// Styles for mobile nav drawer
.mdc-drawer.mobile-drawer {
@include drawer.width(var(--nav-drawer-mobile-width-open));

.mdc-drawer__content {
margin-top: 87px;
}
.mdc-drawer__header {
font-size: 12px;
::slotted(div) {
display: flex;
align-items: center;
gap: 13px;
}
}

& + .mdc-drawer-app-content {
box-shadow: none;
}

& + .mdc-drawer-scrim {
backdrop-filter: blur(4.5px);
background: rgba(167, 183, 212, 0.41);
}
}

.visually-hidden {
position: absolute;
clip-path: inset(50%);
padding: 0;
border: 0;
height: 1px;
width: 1px;
overflow: hidden;
}
}
53 changes: 42 additions & 11 deletions packages/elements/src/components/ino-nav-drawer/ino-nav-drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export class NavDrawer implements ComponentInterface {
*/
private drawerInstance: MDCDrawer;
private drawerEl: HTMLElement;

@Element() el!: HTMLInoNavDrawerElement;

/**
Expand Down Expand Up @@ -67,9 +66,21 @@ export class NavDrawer implements ComponentInterface {
@Prop() a11yLabels?: NavDrawerLabels = {
content: 'Main Navigation',
footer: 'Footer Navigation',
toggleBtn: 'Toggle Navigation'
toggleBtn: 'Toggle Navigation',
};

/**
* If true, the mobile drawer will be shown. Automatically sets the variant to `modal`.
*/
@Prop() isMobile?: boolean = false;

// set the variant based on the isMobile prop
@Watch('isMobile')
handleIsMobileChange() {
if (this.isMobile) {
this.variant = 'modal';
}
}
TobiasHeimGalindo marked this conversation as resolved.
Show resolved Hide resolved

componentDidLoad() {
this.drawerInstance = new MDCDrawer(
Expand All @@ -79,17 +90,28 @@ export class NavDrawer implements ComponentInterface {
if (this.drawerInstance) {
this.drawerInstance.open = this.open || false;
}

this.drawerEl.addEventListener('MDCDrawer:closed', this.closeDrawer);
this.initTabindex('content');
this.initTabindex('footer');

this.modifyMobileItems();
}

disconnectedCallback() {
this.drawerEl.removeEventListener('MDCDrawer:closed', this.closeDrawer);
this.drawerInstance?.destroy();
}

private modifyMobileItems() {
if (!this.isMobile) return;

const navItems = this.el.querySelectorAll('ino-nav-item');

navItems.forEach((item) => {
item.classList.add('mobile-nav-item');
});
}
TobiasHeimGalindo marked this conversation as resolved.
Show resolved Hide resolved

// This listener ensures that only the most recently clicked/selected list item appears as "activated"
@Listen('clickEl')
handleListItemClick(event: CustomEvent) {
Expand Down Expand Up @@ -127,21 +149,23 @@ export class NavDrawer implements ComponentInterface {

private initTabindex(slotName: string) {
const contentElements = this.el.querySelector(`[slot="${slotName}"]`);
const contenListItems = contentElements.querySelectorAll('ino-list-item');
contenListItems[0].attrs = {tabIndex:0}
const contentListItems = contentElements.querySelectorAll('ino-list-item');
contentListItems[0].attrs = { tabIndex: 0 };
}

render() {
const { anchor, variant } = this;

const classDrawer = classNames({
'mdc-drawer': true,
'mdc-drawer--docked': variant === 'docked',
'mdc-drawer--docked': !this.isMobile && this.variant === 'docked',
'mdc-drawer--dismissible':
variant === 'dismissible' || variant === 'docked', // docked is a modifier of MDC's dismissible inoVariant
'mdc-drawer--modal': variant === 'modal',
!this.isMobile &&
(this.variant === 'dismissible' || this.variant === 'docked'), // docked is a modifier of MDC's dismissible inoVariant
'mdc-drawer--modal': this.isMobile || this.variant === 'modal',
'mdc-drawer--anchor-left': anchor === 'left',
'mdc-drawer--anchor-right': anchor === 'right',
'mobile-drawer': this.isMobile, // custom class for mobile drawer
});

const classAppContent = classNames({
Expand Down Expand Up @@ -169,11 +193,16 @@ export class NavDrawer implements ComponentInterface {
<nav class="mdc-drawer__footer" aria-label={this.a11yLabels.footer}>
<slot name="footer"></slot>
<ino-icon-button
class="mdc-drawer__toggle"
class={{
'mdc-drawer__toggle': true,
'visually-hidden': this.isMobile, // Hide visually on mobile, but remains in DOM to meet focus-trap requirements
}}
icon="arrow_right"
tabIndex={this.isMobile ? -1 : null} // Exclude from tab navigation on mobile drawer
aria-hidden={this.isMobile} // Hide from screen readers on mobile as it's only used to prevent focus-trap error and has no functional use
onClick={this.toggleDrawer}
attrs={{
ariaLabel: this.a11yLabels.toggleBtn
ariaLabel: this.a11yLabels.toggleBtn,
}}
/>
</nav>
Expand All @@ -189,7 +218,9 @@ export class NavDrawer implements ComponentInterface {
return (
<Host>
{nav}
{variant === 'modal' && <div class="mdc-drawer-scrim"></div>}
{(this.isMobile || variant === 'modal') && (
<div class="mdc-drawer-scrim"></div>
)}
{main}
</Host>
);
Expand Down
14 changes: 8 additions & 6 deletions packages/elements/src/components/ino-nav-drawer/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,13 @@ class MyComponent extends Component {

## Properties

| Property | Attribute | Description | Type | Default |
| ------------ | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `a11yLabels` | -- | The aria-labels used for content and footer nav elements. https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/navigation_role. | `{ content: string; footer: string; toggleBtn: string; }` | `{ content: 'Main Navigation', footer: 'Footer Navigation', toggleBtn: 'Toggle Navigation' }` |
| `anchor` | `anchor` | Side from which the drawer will appear. Possible values: `left` (default), `right`. | `"left" \| "right"` | `'left'` |
| `open` | `open` | Marks this element as open. (**unmanaged**) | `boolean` | `false` |
| `variant` | `variant` | The variant to use for the drawer Possible values: `docked` (default), `dismissible`, `modal`. | `"dismissible" \| "docked" \| "modal"` | `'docked'` |
| Property | Attribute | Description | Type | Default |
| ------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `a11yLabels` | -- | The aria-labels used for content and footer nav elements. https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/navigation_role. | `{ content: string; footer: string; toggleBtn: string; }` | `{ content: 'Main Navigation', footer: 'Footer Navigation', toggleBtn: 'Toggle Navigation', }` |
| `anchor` | `anchor` | Side from which the drawer will appear. Possible values: `left` (default), `right`. | `"left" \| "right"` | `'left'` |
| `isMobile` | `is-mobile` | If true, the mobile drawer will be shown. Automatically sets the variant to `modal`. | `boolean` | `false` |
| `open` | `open` | Marks this element as open. (**unmanaged**) | `boolean` | `false` |
| `variant` | `variant` | The variant to use for the drawer Possible values: `docked` (default), `dismissible`, `modal`. | `"dismissible" \| "docked" \| "modal"` | `'docked'` |


## Events
Expand Down Expand Up @@ -151,6 +152,7 @@ class MyComponent extends Component {
| -------------------------------------- | ------------------------------------------------------- |
| `--ino-nav-drawer-background` | Background of the drawer. |
| `--ino-nav-drawer-height` | Height of the drawer. |
| `--ino-nav-drawer-mobile-width-open` | Mobile variant only! The width of the open drawer. |
| `--ino-nav-drawer-text-color` | Color of text inside the drawer. |
| `--ino-nav-drawer-timing-function` | Timing function of the slide animation of the drawer. |
| `--ino-nav-drawer-transition-duration` | Duration of the slide animation of the drawer. |
Expand Down
23 changes: 22 additions & 1 deletion packages/elements/src/components/ino-nav-item/ino-nav-item.scss
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
@use '../base/mdc-customize';
@use '@material/list/mdc-list';
@use '../base/theme';
@use '../base/typography-new' as typography;

ino-nav-item  {
ino-nav-item {
/**
* @prop --ino-nav-item-color: Inactive color of icon.
* @prop --ino-nav-item-color-active: Active color of icon.
Expand Down Expand Up @@ -69,4 +70,24 @@ ino-nav-item  {
}
}
}

// Styles for mobile-nav-item
&.mobile-nav-item {
.mdc-deprecated-list-item,
ino-list-item .mdc-deprecated-list-item {
@include typography.typo(label-s);

&__graphic {
margin-right: 0px;
}

// icon
::slotted(ino-icon),
ino-icon,
.ino-list-item__icon {
--icon-width: 16px;
--icon-height: 16px;
}
}
}
}
24 changes: 23 additions & 1 deletion packages/storybook/elements-stencil-docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -5040,7 +5040,7 @@
"reflectToAttr": false,
"docs": "The aria-labels used for content and footer nav elements.\nhttps://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/navigation_role.",
"docsTags": [],
"default": "{\n content: 'Main Navigation',\n footer: 'Footer Navigation',\n toggleBtn: 'Toggle Navigation'\n }",
"default": "{\n content: 'Main Navigation',\n footer: 'Footer Navigation',\n toggleBtn: 'Toggle Navigation',\n }",
"values": [
{
"type": "{ content: string; footer: string; toggleBtn: string; }"
Expand Down Expand Up @@ -5071,6 +5071,23 @@
"optional": true,
"required": false
},
{
"name": "isMobile",
"type": "boolean",
"mutable": false,
"attr": "is-mobile",
"reflectToAttr": false,
"docs": "If true, the mobile drawer will be shown. Automatically sets the variant to `modal`.",
"docsTags": [],
"default": "false",
"values": [
{
"type": "boolean"
}
],
"optional": true,
"required": false
},
{
"name": "open",
"type": "boolean",
Expand Down Expand Up @@ -5138,6 +5155,11 @@
"annotation": "prop",
"docs": "Height of the drawer."
},
{
"name": "--ino-nav-drawer-mobile-width-open",
"annotation": "prop",
"docs": "Mobile variant only! The width of the open drawer."
},
{
"name": "--ino-nav-drawer-text-color",
"annotation": "prop",
Expand Down
7 changes: 7 additions & 0 deletions packages/storybook/src/assets/images/elements.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
.story-nav-drawer__docked_anchor--right,
.story-nav-drawer__dismissible {
position: relative;
height: 500px;
height: 700px;
border: 1px solid grey;
border-radius: 4px;
overflow: hidden;
Expand All @@ -17,4 +17,4 @@

.button__label {
pointer-events: none;
}
}
Loading
Loading