Skip to content

Commit 4b26b60

Browse files
Improve scroll behaviour (#145)
* Improve scroll behaviour with sticky nav * Use sticky element from Mavis
1 parent 7c4b0b0 commit 4b26b60

File tree

5 files changed

+96
-3
lines changed

5 files changed

+96
-3
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// app/assets/javascript/custom-elements/is-sticky.js
2+
3+
// Copied from https://github.com/nhsuk/manage-vaccinations-in-schools-prototype/blob/main/app/assets/javascripts/custom-elements/is-sticky.js
4+
5+
const IsStickyComponent = class extends HTMLElement {
6+
constructor() {
7+
super()
8+
this.stickyElementStyle = null
9+
this.stickyElementTop = 0
10+
11+
this.determineStickyState = this.determineStickyState.bind(this)
12+
this.throttledStickyState = this.throttle(this.determineStickyState, 100)
13+
}
14+
15+
connectedCallback() {
16+
this.stickyElementStyle = window.getComputedStyle(this)
17+
this.stickyElementTop = parseInt(this.stickyElementStyle.top, 10)
18+
19+
window.addEventListener('scroll', this.throttledStickyState)
20+
21+
this.determineStickyState()
22+
}
23+
24+
disconnectedCallback() {
25+
window.removeEventListener('scroll', this.throttledStickyState)
26+
}
27+
28+
determineStickyState() {
29+
const currentTop = this.getBoundingClientRect().top
30+
this.dataset.stuck = String(currentTop <= this.stickyElementTop)
31+
}
32+
33+
throttle(callback, limit) {
34+
let inThrottle
35+
return function () {
36+
const args = arguments
37+
const context = this
38+
if (!inThrottle) {
39+
callback.apply(context, args)
40+
inThrottle = true
41+
setTimeout(() => (inThrottle = false), limit)
42+
}
43+
}
44+
}
45+
}
46+
47+
customElements.define('is-sticky', IsStickyComponent)

app/assets/sass/components/_status-bar.scss

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,45 @@
4444
.app-status-bar__key {
4545
@include nhsuk-typography-weight-bold($important: true);
4646
opacity: 0.9;
47+
}
48+
49+
is-sticky {
50+
display: block;
51+
position: sticky;
52+
top: 0;
53+
z-index: 999;
54+
}
55+
56+
// Sass variables for header height calculations
57+
$nav-height: 48px;
58+
$status-bar-base-padding: 16px; // 8px top + 8px bottom
59+
$status-bar-row-height: 32px; // Approximate row height
60+
$status-bar-row-spacing: 16px; // 8px margin + 8px padding for borders
61+
62+
// Function to calculate header height for given number of rows
63+
@function calculate-header-height($rows) {
64+
$spacing-multiplier: $rows - 1; // Number of border spacings needed
65+
@if $spacing-multiplier < 0 {
66+
$spacing-multiplier: 0;
67+
}
68+
@return $nav-height + $status-bar-base-padding + ($rows * $status-bar-row-height) + ($spacing-multiplier * $status-bar-row-spacing);
69+
}
70+
71+
// Default scroll padding (fallback for 3 rows)
72+
html {
73+
scroll-padding-top: #{calculate-header-height(3)};
74+
}
75+
76+
// Generate classes for different row counts
77+
@for $rowCount from 1 through 5 {
78+
html:has(.app-status-bar--rows-#{$rowCount}) {
79+
scroll-padding-top: #{calculate-header-height($rowCount)};
80+
}
81+
}
82+
83+
// Alternative approach if :has() doesn't work - classes on html element
84+
@for $rowCount from 1 through 5 {
85+
html.sticky-header-rows-#{$rowCount} {
86+
scroll-padding-top: #{calculate-header-height($rowCount)};
87+
}
4788
}

app/views/_components/status-bar/template.njk

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
{# app/views/_components/status-bar/template.njk #}
22

3-
<div class="app-status-bar">
3+
{# Output a class counting the number of rows so that we can work out how tall the sticky nav is #}
4+
{% set classes = params.classes + (" app-status-bar--rows-" + params.rows | length) %}
5+
6+
<div class="app-status-bar {{ classes }}">
47
<div class="nhsuk-width-container">
58
{% for row in params.rows %}
69
<div class="app-status-bar__row">
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{# app/views/_includes/event-active-header.njk #}
22

3-
<div class="app-appointment-header app-appointment-header--sticky" data-stuck="true">
3+
<is-sticky class="app-appointment-header app-appointment-header--sticky">
44
{% include "_includes/appointment-status-bar.njk" %}
55

66
<div class="nhsuk-width-container">
77
{% include "_includes/event-active-tabs.njk" %}
88
</div>
9-
</div>
9+
</is-sticky>

app/views/_includes/scripts.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
<script src="/js/jquery-3.5.1.min.js" type="module"></script>
2+
<script src="/js/custom-elements/is-sticky.js"></script>
23
<script src="/js/main.js" type="module"></script>
34
{% if useAutoStoreData %}
45
<script src="/js/auto-store-data.js" type="module"></script>
56
{% endif %}
67
<script src="/js/button-menu.js" type="module"></script>
8+
79
<script src="/js/modal.js"></script>
810
<script src="/js/scroll-to-section.js"></script>
911
<script src="/js/expanded-state-tracker.js"></script>

0 commit comments

Comments
 (0)