diff --git a/__tests__/Vuent.test.js b/__tests__/Vuent.test.js
index 7980531..d96a847 100644
--- a/__tests__/Vuent.test.js
+++ b/__tests__/Vuent.test.js
@@ -10,6 +10,7 @@ import {
VntIcon,
VntInput,
VntLink,
+ VntNavView,
VntRadio,
VntRating,
VntSelect,
@@ -43,13 +44,14 @@ describe('Vuent', () => {
expect(isInstalled(localVue, VntIcon)).toBe(true);
expect(isInstalled(localVue, VntInput)).toBe(true);
expect(isInstalled(localVue, VntLink)).toBe(true);
+ expect(isInstalled(localVue, VntNavView)).toBe(true);
expect(isInstalled(localVue, VntRadio)).toBe(true);
expect(isInstalled(localVue, VntRating)).toBe(true);
expect(isInstalled(localVue, VntSelect)).toBe(true);
expect(isInstalled(localVue, VntSlider)).toBe(true);
expect(isInstalled(localVue, VntToggle)).toBe(true);
expect(isInstalled(localVue, VntTabs)).toBe(true);
- expect(countInstalledPlugins(localVue)).toBe(14);
+ expect(countInstalledPlugins(localVue)).toBe(15);
});
test('has $vuent instance object', () => {
diff --git a/__tests__/__snapshots__/navview.test.js.snap b/__tests__/__snapshots__/navview.test.js.snap
new file mode 100644
index 0000000..13d3f1f
--- /dev/null
+++ b/__tests__/__snapshots__/navview.test.js.snap
@@ -0,0 +1,113 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`NavView by default renders correctly 1`] = `
+
+
+
+
+
+
+
+`;
+
+exports[`NavView generates correct structure renders correctly 1`] = `
+
+
+
+
+
+
+
+`;
+
+exports[`NavView when using custom pane footer renders correctly 1`] = `
+
+
+
+
+
+
+
+`;
+
+exports[`NavView when using custom pane header renders correctly 1`] = `
+
+
+
+
+
+
+
+`;
diff --git a/__tests__/__snapshots__/navviewItem.test.js.snap b/__tests__/__snapshots__/navviewItem.test.js.snap
new file mode 100644
index 0000000..a0f6db1
--- /dev/null
+++ b/__tests__/__snapshots__/navviewItem.test.js.snap
@@ -0,0 +1,13 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`NavviewItem by default renders correctly 1`] = `
+
+
+
+`;
+
+exports[`NavviewItem when icon is set renders correctly 1`] = `
+
+ Settings
+
+`;
diff --git a/__tests__/navview.test.js b/__tests__/navview.test.js
new file mode 100644
index 0000000..b059f71
--- /dev/null
+++ b/__tests__/navview.test.js
@@ -0,0 +1,128 @@
+import { createLocalVue, mount } from '@vue/test-utils';
+import { VntNavView } from '@/components';
+import { isInstalled } from './utils';
+
+describe('NavView', () => {
+ let localVue, wrapper;
+
+ beforeAll(() => {
+ localVue = createLocalVue();
+ localVue.use(VntNavView);
+ });
+
+ test('can be installed separately', () => {
+ expect(isInstalled(localVue, VntNavView)).toBe(true);
+ });
+
+ describe('by default', () => {
+
+ beforeAll(() => {
+ wrapper = mount(VntNavView, {
+ localVue
+ });
+ });
+
+ test('pane is hidden', () => {
+ expect(wrapper.vm.isPaneOpened).toBe(false);
+ });
+
+ test('renders correctly', () => {
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ });
+
+ describe('generates correct structure', () => {
+
+ beforeAll(() => {
+ wrapper = mount(VntNavView, {
+ localVue,
+ propsData: {
+ paneTitle: 'App name',
+ header: 'Header',
+ },
+ slots: {
+ default: `
+
+ Nav item header
+ Link 1
+
+ Link 2
+
+
+ Some content
+
+ `
+ }
+ });
+ });
+
+ test('renders correctly', () => {
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ test('pane can be toggled', () => {
+ const menuButton = wrapper.find('.vnt-navview__pane-toggle');
+ menuButton.trigger('click');
+
+ expect(wrapper.vm.isPaneOpened).toBe(true);
+ });
+
+ });
+
+ describe('when using custom pane header', () => {
+
+ beforeAll(() => {
+ wrapper = mount(VntNavView, {
+ localVue,
+ propsData: {
+ paneTitle: 'App name'
+ },
+ slots: {
+ default: `
+
+ App name with logo
+
+
+ Some content
+
+ `
+ }
+ });
+ });
+
+ test('renders correctly', () => {
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ });
+
+ describe('when using custom pane footer', () => {
+
+ beforeAll(() => {
+ wrapper = mount(VntNavView, {
+ localVue,
+ propsData: {
+ paneTitle: 'App name',
+ },
+ slots: {
+ default: `
+
+ Account
+ Settings
+
+
+ Some content
+
+ `
+ }
+ });
+ });
+
+ test('renders correctly', () => {
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ });
+
+});
diff --git a/__tests__/navviewItem.test.js b/__tests__/navviewItem.test.js
new file mode 100644
index 0000000..803cbff
--- /dev/null
+++ b/__tests__/navviewItem.test.js
@@ -0,0 +1,76 @@
+import { mount } from '@vue/test-utils';
+import VntNavviewItem from '@/components/navview/MenuItem.vue';
+
+describe('NavviewItem', () => {
+ let wrapper;
+
+ describe('by default', () => {
+
+ beforeAll(() => {
+ wrapper = mount(VntNavviewItem);
+ });
+
+ test('renders correctly', () => {
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ test('is not active', () => {
+ expect(wrapper.vm.active).toBe(false);
+ });
+
+ test('icon is null', () => {
+ expect(wrapper.vm.icon).toBeNull();
+ });
+
+ test('click handler is undefined', () => {
+ expect(wrapper.vm.click).toBeUndefined();
+ });
+
+ test('when clicked, no error is thrown', () => {
+ expect(() => {
+ wrapper.find('a').trigger('click');
+ }).not.toThrow();
+ });
+
+ });
+
+ describe('when icon is set', () => {
+
+ beforeAll(() => {
+ wrapper = mount(VntNavviewItem, {
+ propsData: {
+ icon: 'settings'
+ },
+ slots: {
+ default: 'Settings'
+ }
+ });
+ });
+
+ test('renders correctly', () => {
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ });
+
+ describe('when click handler is given', () => {
+ let mockClick;
+
+ beforeAll(() => {
+ mockClick = jest.fn();
+
+ wrapper = mount(VntNavviewItem, {
+ propsData: {
+ click: mockClick
+ }
+ });
+ });
+
+ test('when clicked, click handler is invoked', () => {
+ wrapper.find('a').trigger('click');
+ expect(mockClick).toHaveBeenCalled();
+ });
+
+ });
+
+});
diff --git a/commitlint.config.js b/commitlint.config.js
index c0a8bf8..279fd24 100644
--- a/commitlint.config.js
+++ b/commitlint.config.js
@@ -11,6 +11,7 @@ module.exports = {
'icon',
'input',
'link',
+ 'navview',
'radio',
'rating',
'select',
diff --git a/src/components/index.js b/src/components/index.js
index 9fb94ee..7fd0270 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -7,6 +7,7 @@ export { default as VntHeader } from './header';
export { default as VntIcon } from './icon';
export { default as VntInput } from './input';
export { default as VntLink } from './link';
+export { default as VntNavView } from './navview';
export { default as VntRadio } from './radio';
export { default as VntRating } from './rating';
export { default as VntSelect } from './select';
diff --git a/src/components/navview/MenuButton.vue b/src/components/navview/MenuButton.vue
new file mode 100644
index 0000000..b7bc5e5
--- /dev/null
+++ b/src/components/navview/MenuButton.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
diff --git a/src/components/navview/MenuItem.vue b/src/components/navview/MenuItem.vue
new file mode 100644
index 0000000..73f6af5
--- /dev/null
+++ b/src/components/navview/MenuItem.vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/navview/MenuItemHeader.vue b/src/components/navview/MenuItemHeader.vue
new file mode 100644
index 0000000..0f28358
--- /dev/null
+++ b/src/components/navview/MenuItemHeader.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
diff --git a/src/components/navview/MenuItemSeparator.vue b/src/components/navview/MenuItemSeparator.vue
new file mode 100644
index 0000000..dd58f98
--- /dev/null
+++ b/src/components/navview/MenuItemSeparator.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/src/components/navview/NavView.vue b/src/components/navview/NavView.vue
new file mode 100644
index 0000000..79dc04e
--- /dev/null
+++ b/src/components/navview/NavView.vue
@@ -0,0 +1,239 @@
+
+
+
+
+
+
+
+
+ Content goes here
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/navview/README.md b/src/components/navview/README.md
new file mode 100644
index 0000000..0fe9c41
--- /dev/null
+++ b/src/components/navview/README.md
@@ -0,0 +1,25 @@
+# NavView
+
+## How to use
+
+```html
+
+
+ App name with logo
+
+
+ Nav item header
+ Link 1
+ Link 2
+
+ Link 3
+
+
+ Account
+ Settings
+
+
+ Content
+
+
+```
diff --git a/src/components/navview/index.js b/src/components/navview/index.js
new file mode 100644
index 0000000..c287d77
--- /dev/null
+++ b/src/components/navview/index.js
@@ -0,0 +1,13 @@
+import NavView from './NavView.vue';
+import NavViewMenuItem from './MenuItem.vue';
+import NavViewMenuItemHeader from './MenuItemHeader.vue';
+import NavViewMenuItemSeparator from './MenuItemSeparator.vue';
+
+NavView.install = function (Vue) {
+ Vue.component(NavViewMenuItemHeader.name, NavViewMenuItemHeader);
+ Vue.component(NavViewMenuItem.name, NavViewMenuItem);
+ Vue.component(NavViewMenuItemSeparator.name, NavViewMenuItemSeparator);
+ Vue.component(NavView.name, NavView);
+};
+
+export default NavView;
diff --git a/src/scss/mixins/_text.scss b/src/scss/mixins/_text.scss
new file mode 100644
index 0000000..bc2e353
--- /dev/null
+++ b/src/scss/mixins/_text.scss
@@ -0,0 +1,5 @@
+@mixin text-overflow() {
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+}