Skip to content

Commit f2aa99a

Browse files
authored
Merge pull request #1285 from TechnologyEnhancedLearning/Develop/Features/TD-5762-Left-hand-navigation-implementation
Left hand navigation implementation
2 parents 4ae759a + 08789f7 commit f2aa99a

File tree

7 files changed

+378
-1
lines changed

7 files changed

+378
-1
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace LearningHub.Nhs.WebUI.Models.SideMenu
2+
{
3+
using System.Collections.Generic;
4+
using Microsoft.AspNetCore.Routing;
5+
6+
/// <summary>
7+
/// Defines the <see cref="SideNavViewModel" />.
8+
/// </summary>
9+
public class SideNavViewModel
10+
{
11+
/// <summary>
12+
/// Gets or sets the Groups.
13+
/// </summary>
14+
public List<SideNavigationGroup> Groups { get; set; } = [];
15+
16+
/// <summary>
17+
/// Gets or sets the RouteData.
18+
/// </summary>
19+
public RouteValueDictionary RouteData { get; set; } = [];
20+
}
21+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
namespace LearningHub.Nhs.WebUI.Models.SideMenu
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using Microsoft.AspNetCore.Routing;
7+
8+
/// <summary>
9+
/// Defines the <see cref="SideNavigationConfiguration" />.
10+
/// </summary>
11+
public static class SideNavigationConfiguration
12+
{
13+
/// <summary>
14+
/// GetGroupedMenus.
15+
/// </summary>
16+
/// <returns>IEnumerable.</returns>
17+
public static IEnumerable<SideNavigationGroup> GetGroupedMenus()
18+
{
19+
return new List<SideNavigationGroup>
20+
{
21+
new SideNavigationGroup
22+
{
23+
GroupTitle = "Account",
24+
Items = new List<SideNavigationItem>
25+
{
26+
new SideNavigationItem
27+
{
28+
Text = "Personal details",
29+
Controller = "Account",
30+
Action = "PersonalDetails",
31+
IsActive = route => MatchRoute(route, "Account", "PersonalDetails"),
32+
},
33+
new SideNavigationItem
34+
{
35+
Text = "My employment",
36+
Controller = "Account",
37+
Action = "MyEmployment",
38+
IsActive = route => MatchRoute(route, "Account", "MyEmployment"),
39+
},
40+
new SideNavigationItem
41+
{
42+
Text = "Security",
43+
Controller = "Account",
44+
Action = "Security",
45+
IsActive = route => MatchRoute(route, "Account", "Security"),
46+
},
47+
new SideNavigationItem
48+
{
49+
Text = "Notification",
50+
Controller = "Account",
51+
Action = "Notification",
52+
IsActive = route => MatchRoute(route, "Account", "Notification"),
53+
},
54+
},
55+
},
56+
new SideNavigationGroup
57+
{
58+
GroupTitle = "Activity",
59+
Items = new List<SideNavigationItem>
60+
{
61+
new SideNavigationItem
62+
{
63+
Text = "Recent",
64+
Controller = "Activity",
65+
Action = "Recent",
66+
IsActive = route => MatchRoute(route, "Activity", "Recent"),
67+
},
68+
new SideNavigationItem
69+
{
70+
Text = "Bookmark",
71+
Controller = "Activity",
72+
Action = "Bookmark",
73+
IsActive = route => MatchRoute(route, "Activity", "Bookmark"),
74+
},
75+
new SideNavigationItem
76+
{
77+
Text = "Certificates",
78+
Controller = "Activity",
79+
Action = "Certificates",
80+
IsActive = route => MatchRoute(route, "Activity", "Certificates"),
81+
},
82+
new SideNavigationItem
83+
{
84+
Text = "Learning history",
85+
Controller = "Activity",
86+
Action = "Learninghistory",
87+
IsActive = route => MatchRoute(route, "Activity", "Learninghistory"),
88+
},
89+
},
90+
},
91+
};
92+
}
93+
94+
/// <summary>
95+
/// GetMenuGroupByTitle.
96+
/// </summary>
97+
/// <param name="title">title.</param>
98+
/// <returns>string.</returns>
99+
public static SideNavigationGroup? GetMenuGroupByTitle(string title)
100+
{
101+
return GetGroupedMenus().FirstOrDefault(g =>
102+
string.Equals(g.GroupTitle, title, StringComparison.OrdinalIgnoreCase));
103+
}
104+
105+
private static bool MatchRoute(RouteValueDictionary route, string controller, string action)
106+
{
107+
var currentController = route["controller"]?.ToString();
108+
var currentAction = route["action"]?.ToString();
109+
110+
return string.Equals(currentController, controller, StringComparison.OrdinalIgnoreCase) &&
111+
string.Equals(currentAction, action, StringComparison.OrdinalIgnoreCase);
112+
}
113+
}
114+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace LearningHub.Nhs.WebUI.Models.SideMenu
2+
{
3+
using System.Collections.Generic;
4+
5+
/// <summary>
6+
/// Defines the <see cref="SideNavigationGroup" />.
7+
/// </summary>
8+
public class SideNavigationGroup
9+
{
10+
/// <summary>
11+
/// Gets or sets a value indicating GroupTitle.
12+
/// </summary>
13+
public string GroupTitle { get; set; } = string.Empty;
14+
15+
/// <summary>
16+
/// Gets or sets a Items.
17+
/// </summary>
18+
public List<SideNavigationItem> Items { get; set; } = [];
19+
}
20+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
namespace LearningHub.Nhs.WebUI.Models.SideMenu
2+
{
3+
using System;
4+
using Microsoft.AspNetCore.Routing;
5+
6+
/// <summary>
7+
/// Defines the <see cref="SideNavigationItem" />.
8+
/// </summary>
9+
public class SideNavigationItem
10+
{
11+
/// <summary>
12+
/// Gets or sets a value indicating Text.
13+
/// </summary>
14+
public string Text { get; set; } = string.Empty;
15+
16+
/// <summary>
17+
/// Gets or sets a value indicating Controller.
18+
/// </summary>
19+
public string Controller { get; set; } = string.Empty;
20+
21+
/// <summary>
22+
/// Gets or sets a value indicating Action.
23+
/// </summary>
24+
public string Action { get; set; } = "Index";
25+
26+
/// <summary>
27+
/// Gets or sets a value indicating IsActiven.
28+
/// </summary>
29+
public Func<RouteValueDictionary, bool> IsActive { get; set; }
30+
}
31+
}

LearningHub.Nhs.WebUI/Styles/nhsuk/layout.scss

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,94 @@ button[data-toggle="modal"] {
254254
li.autosuggestion-option:last-of-type {
255255
border-bottom: none !important;
256256
}
257+
258+
259+
/* side navigation styles */
260+
.side-nav__list {
261+
list-style: none;
262+
margin: 0;
263+
padding: 0;
264+
border-right: 1px solid #d8dde0;
265+
}
266+
267+
.side-nav__item {
268+
padding: 12px 16px;
269+
border-bottom: 1px solid #d8dde0;
270+
}
271+
272+
.side-nav__item--active a {
273+
font-weight: bold;
274+
text-decoration: underline;
275+
}
276+
277+
.side-nav__toggle,
278+
.side-nav__close {
279+
display: none;
280+
background: #e8f0f7;
281+
color: #005eb8;
282+
border: none;
283+
font-size: 16px;
284+
padding: 10px 16px;
285+
border-radius: 25px;
286+
margin: 10px;
287+
cursor: pointer;
288+
}
289+
290+
.menu-icon {
291+
display: inline-flex;
292+
justify-content: center;
293+
align-items: center;
294+
width: 20px;
295+
height: 20px;
296+
box-sizing: border-box;
297+
padding: 0;
298+
border: 1px solid #d8dde0;
299+
border-radius: 50%;
300+
background-color: white;
301+
transition: background-color 0.3s ease;
302+
}
303+
304+
305+
.side-nav__container {
306+
position: fixed;
307+
top: 0;
308+
left: 0;
309+
width: 80%;
310+
max-width: 320px;
311+
height: 100%;
312+
background-color: #e8f0f7;
313+
border-right: 40px solid #005eb8;
314+
box-sizing: border-box;
315+
transform: translateX(-100%);
316+
transition: transform 0.3s ease-in-out;
317+
z-index: 1000;
318+
box-shadow: 4px 0 10px rgba(0, 0, 0, 0.1);
319+
display: flex;
320+
flex-direction: column;
321+
overflow: visible;
322+
}
323+
324+
325+
.side-nav__close {
326+
position: relative;
327+
right: -35px;
328+
z-index: 2;
329+
align-self: flex-end;
330+
text-align: right;
331+
}
332+
333+
334+
.nav-toggle:checked + label.side-nav__toggle + .side-nav__container {
335+
transform: translateX(0);
336+
}
337+
338+
339+
.nav-toggle:checked + label .menu-icon {
340+
transform: rotate(180deg);
341+
}
342+
343+
344+
257345
/* large desktop */
258346
@media (min-width: px2rem(990)) {
259347

@@ -271,6 +359,26 @@ li.autosuggestion-option:last-of-type {
271359
}
272360
}
273361

362+
@media (min-width: px2rem(768)) {
363+
.nav-toggle,
364+
.side-nav__toggle,
365+
.side-nav__close {
366+
display: none !important;
367+
}
368+
369+
.side-nav__container {
370+
position: static;
371+
transform: none !important;
372+
height: auto;
373+
box-shadow: none;
374+
display: block;
375+
width: auto;
376+
background-color: #ffffff;
377+
border-right: none;
378+
}
379+
}
380+
381+
274382
/* small desktop */
275383
@media (max-width: px2rem(989)) {
276384

@@ -495,6 +603,20 @@ li.autosuggestion-option:last-of-type {
495603
.nhsuk-width-container.nhsuk-header__container.app-width-container {
496604
padding-bottom: 0;
497605
}
606+
607+
608+
.side-nav__toggle, .side-nav__close {
609+
display: inline-block;
610+
}
611+
612+
.side-nav__close {
613+
background-color: transparent;
614+
}
615+
616+
.nav-toggle:checked + label.side-nav__toggle .menu-icon,
617+
.nav-toggle:checked + label.side-nav__toggle + .side-nav__container .side-nav__close .menu-icon {
618+
background-color: #e8f0f7;
619+
}
498620
}
499621

500622
/* mobile */
@@ -596,5 +718,5 @@ li.autosuggestion-option:last-of-type {
596718

597719
.autosuggestion-menu {
598720
top: 100%;
599-
}
721+
}
600722
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
namespace LearningHub.Nhs.WebUI.ViewComponents
2+
{
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using LearningHub.Nhs.WebUI.Models.SideMenu;
6+
using Microsoft.AspNetCore.Mvc;
7+
using Microsoft.AspNetCore.Mvc.Rendering;
8+
9+
/// <summary>
10+
/// Initializes a new instance of the <see cref="SideNavViewComponent"/> class.
11+
/// </summary>
12+
public class SideNavViewComponent : ViewComponent
13+
{
14+
/// <summary>
15+
/// The Invoke.
16+
/// </summary>
17+
/// <param name="groupTitle">group Title.</param>
18+
/// <returns>A representing the result of the synchronous operation.</returns>
19+
public IViewComponentResult Invoke(string? groupTitle = null)
20+
{
21+
var routeData = this.ViewContext.RouteData.Values;
22+
var groups = string.IsNullOrEmpty(groupTitle)
23+
? SideNavigationConfiguration.GetGroupedMenus().ToList()
24+
: SideNavigationConfiguration.GetMenuGroupByTitle(groupTitle) is { } singleGroup
25+
? new List<SideNavigationGroup> { singleGroup }
26+
: new List<SideNavigationGroup>();
27+
28+
var viewModel = new SideNavViewModel
29+
{
30+
Groups = groups,
31+
RouteData = routeData,
32+
};
33+
return this.View(viewModel);
34+
}
35+
}
36+
}

0 commit comments

Comments
 (0)