Skip to content

Commit

Permalink
⬆️ Back To Top Link ⬆️ (#2990)
Browse files Browse the repository at this point in the history
* Page template for length

* Add macro

* Add component foundations

* Build stylesheet

* Add additional functionality

* Improved functionality

* Fix border and layout

* Fix resize behaviour

* Changes to CSS

* Add more tests

* Update VR Tests

* Change to full width

---------

Co-authored-by: Alessio Venturini <112873190+alessioventuriniAND@users.noreply.github.com>
  • Loading branch information
adi-unni and alessioventuriniAND authored Feb 28, 2024
1 parent 502787b commit 367afbc
Show file tree
Hide file tree
Showing 20 changed files with 557 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/check-labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ jobs:
steps:
- uses: docker://onsdigital/github-pr-label-checker:v1.2.7
with:
one_of: Breaking changes,Accessibility,Bug,Documentation,Dependencies,Enhancement,Example
one_of: Breaking changes,Accessibility,Bug,Documentation,Dependencies,Enhancement,Example,Component,Pattern
none_of: Awaiting resource,Do not merge,Duplicate,Needs validating,Not doing
repo_token: ${{ secrets.GITHUB_TOKEN }}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions src/components/back-to-top/_back-to-top.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
.ons-back-to-top {
&__description {
margin-left: 0.2rem;
}

&__enabled {
background: none;
width: fit-content;
padding: 0.5em 0;
}

&__sticky {
bottom: -1px;
position: fixed;
z-index: 10;
background: var(--ons-color-grey-15);
width: 100%;
height: fit-content;
left: 0;

> .ons-back-to-top__link {
width: 100%;
padding: 0.7em 0;
position: relative;
display: block;
color: var(--ons-color-black);

&:focus {
box-shadow: none;
text-decoration: underline solid var(--ons-color-text-link-focus) 4px;
}
}
}
}
4 changes: 4 additions & 0 deletions src/components/back-to-top/_macro-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
| Name | Type | Required | Description |
| ----------- | ------ | -------- | -------------------------------------------------------------- |
| description | string | false | The text label added to the button. Defaults to "Back to Top" |
| anchor | string | false | The 'id' of the element the button jumps to. Defaults to "Top" |
17 changes: 17 additions & 0 deletions src/components/back-to-top/_macro.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{% from "components/icon/_macro.njk" import onsIcon %}

{% macro onsBackToTop(params) %}
<div class="ons-back-to-top ons-js-back-to-top ons-back-to-top__enabled">
<a href="#{{ params.anchor | default('top') }}" class="ons-back-to-top__link">
<span>
{{-
onsIcon({
"iconType": 'arrow-up',
"iconSize": 'xs'
})
-}}
<span class="ons-back-to-top__description">{{ params.description | default('Back to top') }}</span>
</span>
</a>
</div>
{% endmacro %}
60 changes: 60 additions & 0 deletions src/components/back-to-top/_macro.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/** @jest-environment jsdom */

import * as cheerio from 'cheerio';

import axe from '../../tests/helpers/axe';
import { renderComponent } from '../../tests/helpers/rendering';

describe('macro: back-to-top', () => {
it('passes jest-axe checks', async () => {
const $ = cheerio.load(
renderComponent('back-to-top', {
description: 'Back to top',
anchor: 'example-target',
}),
);

const results = await axe($.html());
expect(results).toHaveNoViolations();
});

it('has back to top link with the provided description', async () => {
const $ = cheerio.load(
renderComponent('back-to-top', {
description: 'Scroll to top',
anchor: 'example-target',
}),
);
expect($('.ons-back-to-top .ons-back-to-top__link').text().trim()).toBe('Scroll to top');
});

it('has back to top link with the provided anchor', async () => {
const $ = cheerio.load(
renderComponent('back-to-top', {
description: 'Back to top',
anchor: 'example-target',
}),
);

expect($('.ons-back-to-top .ons-back-to-top__link').attr('href')).toBe('#example-target');
});

it('has back to top link with the default description if no description provided', async () => {
const $ = cheerio.load(
renderComponent('back-to-top', {
anchor: 'example-target',
}),
);

expect($('.ons-back-to-top .ons-back-to-top__link').text().trim()).toBe('Back to top');
});

it('has back to top link with the default anchor if no anchor provided', async () => {
const renderedComponent = renderComponent('back-to-top', {
description: 'Back to top',
});
const $ = cheerio.load(renderedComponent);

expect($('.ons-back-to-top .ons-back-to-top__link').attr('href')).toBe('#top');
});
});
12 changes: 12 additions & 0 deletions src/components/back-to-top/back-to-top.dom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import domready from '../../js/domready';

async function backToTop() {
const bttElement = [...document.querySelectorAll('.ons-js-back-to-top')];

if (bttElement) {
const Btt = (await import('./back-to-top')).default;
bttElement.forEach((btn) => new Btt(btn));
}
}

domready(backToTop);
58 changes: 58 additions & 0 deletions src/components/back-to-top/back-to-top.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
export default class backToTop {
constructor(component) {
this.component = component;
this.content = this.component.previousElementSibling;
this.target = document.getElementById(this.component.firstElementChild.href.split('#')[1]);
this.contentleft;
this.updateContentDetails();

this.handleScroll = this.handleScroll.bind(this);
window.addEventListener('scroll', this.handleScroll);

window.addEventListener('resize', () => {
this.setEnabled();
this.updateContentDetails();
this.handleScroll();
});

this.component.addEventListener('click', () => {
this.component.firstElementChild.blur();
});
}

handleScroll() {
let scrollPosition = window.scrollY + window.innerHeight;

if (this.target) {
scrollPosition = -this.target.getBoundingClientRect().top + window.innerHeight;
}

const contentRect = this.content.getBoundingClientRect();
const windowHeight = window.innerHeight;
const contentBottom = contentRect.bottom;

const stickyThreshold = windowHeight * 2;
if (scrollPosition > stickyThreshold && windowHeight < contentBottom) {
this.setSticky();
} else {
this.setEnabled();
}
}

setSticky() {
this.component.classList.remove('ons-back-to-top__enabled');
this.component.classList.add('ons-back-to-top__sticky');
this.component.firstElementChild.children[0].style.marginLeft = `${this.contentleft}px`;
}

setEnabled() {
this.updateContentDetails();
this.component.classList.remove('ons-back-to-top__sticky');
this.component.classList.add('ons-back-to-top__enabled');
this.component.firstElementChild.children[0].style.marginLeft = '';
}

updateContentDetails() {
this.contentleft = this.component.getBoundingClientRect().left;
}
}
Loading

0 comments on commit 367afbc

Please sign in to comment.