Skip to content
Snippets Groups Projects
Unverified Commit 637e97be authored by Olivia Simonet's avatar Olivia Simonet Committed by GitHub
Browse files

Permettre d'ouvrir la modale de recherche avec les différentes options de config (#798)


Co-authored-by: default avatarAlexis BENOIT <alex@noesya.coop>
parent 7a05e568
No related branches found
No related tags found
No related merge requests found
Showing
with 277 additions and 329 deletions
......@@ -76,8 +76,10 @@ class MainMenu {
}
resize () {
document.documentElement.style.setProperty('--header-height', this.element.offsetHeight + 'px');
document.documentElement.style.setProperty('--header-menu-max-height', window.innerHeight - this.element.offsetHeight + 'px');
if (!this.state.isOpened) {
document.documentElement.style.setProperty('--header-height', this.element.offsetHeight + 'px');
document.documentElement.style.setProperty('--header-menu-max-height', window.innerHeight - this.element.offsetHeight + 'px');
}
// is state changed ?
if (this.state.isMobile === isMobile()) {
......
/* eslint-disable no-undef */
import { ariaHideBodyChildren } from '../utils/a11y';
import { focusTrap } from '../utils/focus-trap';
/* eslint-disable no-undef, no-underscore-dangle */
window.osuny = window.osuny || {};
window.osuny.Search = window.osuny.Search || {};
const CLASSES = {
modalOpened: 'has-search-opened'
window.osuny.Search = function (element) {
window.osuny.Popup.call(this, element);
this.buttons = document.querySelectorAll('.search-button');
this._setup();
this._listen();
};
window.osuny.Search.prototype = Object.create(window.osuny.Popup.prototype);
window.osuny.Search.prototype._setup = function () {
this.setPageFind();
this.setAccessibility();
};
window.osuny.Search.prototype.setPageFind = function () {
this.pageFindUI = new PagefindUI({
element: this.element,
showSubResults: true
});
// Listen to user's input
this.input = document.querySelector('.pagefind-ui__search-input');
};
class Search {
constructor (container) {
this.state = {
isOpened: false
};
this.button = document.querySelector('.pagefind-ui__toggle');
this.element = document.querySelector('.search__modal');
this.closeButton = this.element.querySelector('.search__close');
// this.a11yButton = document.querySelector('[href="#search-button"]');
this.searchInstance = new PagefindUI({
element: container,
showSubResults: true
});
if (!this.element) {
return;
window.osuny.Search.prototype._listen = function () {
var inPageWithToc = document.body.querySelector('.toc-cta');
window.osuny.Popup.prototype._listen.call(this);
this.buttons.forEach(function (button) {
button.addEventListener('click', this.toggle.bind(this, true, button));
if (inPageWithToc) {
button.classList.add('in-page-with-toc');
}
}.bind(this));
this.listen();
this.setAccessibility();
}
this.input.addEventListener('input', this.onInputChange.bind(this));
setAccessibility () {
const input = document.querySelector('.pagefind-ui__search-input'),
// Delay before screen readers speak results message
speakDelay = 1500;
input.setAttribute('title', input.getAttribute('placeholder'));
// Add element to alert screen reader of the search results
this.accessibleMessageContainer = document.createElement('div');
this.element.append(this.accessibleMessageContainer);
this.accessibleMessageContainer.setAttribute('aria-live', 'polite');
this.accessibleMessageContainer.setAttribute('aria-atomic', 'true');
this.accessibleMessageContainer.classList.add('sr-only', 'pagefind-ui__accessible-message');
input.addEventListener('input', () => {
this.accessibleMessageContainer.innerHTML = '';
if (input.value !== '') {
this.accessibleMessage = document.createElement('p');
this.accessibleMessageContainer.appendChild(this.accessibleMessage);
}
clearTimeout(this.a11yTimeout);
this.a11yTimeout = setTimeout(this.updateAccessibility.bind(this), speakDelay);
});
}
this.observeMutations();
};
updateAccessibility () {
this.updateAccessibilityMessageRole();
this.updateAccessibilityTags();
window.osuny.Search.prototype.observeMutations = function () {
var previousResults = [],
results = [],
firstResult;
this.observer = new MutationObserver(function () {
results = this.element.querySelectorAll('.pagefind-ui__result');
if (results.length > 0 && previousResults.length > 0 && results.length !== previousResults.length) {
firstResult = results[previousResults.length];
setTimeout(this.focusFirstNextResult.bind(firstResult), 100);
}
previousResults = results;
}.bind(this));
this.observer.observe(this.element, { childList: true, subtree: true });
};
window.osuny.Search.prototype.focusFirstNextResult = function () {
var anchor = this.querySelector('a');
if (anchor) {
anchor.focus();
}
};
updateAccessibilityTags () {
const results = this.element.querySelectorAll('.pagefind-ui__result');
window.osuny.Search.prototype.onInputChange = function () {
// Delay before screen readers speak results message
var speakDelay = 1500;
results.forEach(this.updateAccessibilityResult);
// Clear aria live
this.accessibleMessageContainer.innerHTML = '';
clearTimeout(this.a11yTimeout);
this.a11yTimeout = setTimeout(this.updateAccessibility.bind(this), speakDelay);
};
window.osuny.Search.prototype.toggle = function (open, triggerElement) {
window.osuny.Popup.prototype.toggle.call(this, open, triggerElement);
if (this.state.opened) {
this.input.focus();
} else {
this.clear();
}
};
updateAccessibilityResult (result) {
let image = result.querySelector('img'),
title = result.querySelector('.pagefind-ui__result-title');
window.osuny.Search.prototype.clear = function () {
var button = this.element.querySelector('.pagefind-ui__button'),
message = this.element.querySelector('.pagefind-ui__message'),
results = this.element.querySelector('.pagefind-ui__results');
if (image) {
image.setAttribute('alt', '');
}
if (title) {
title.setAttribute('role', 'heading');
title.setAttribute('aria-level', '2');
}
if (this.input) {
this.input.value = '';
}
updateAccessibilityMessageRole () {
const message = this.element.querySelector('.pagefind-ui__message');
if (!message) {
return;
}
message.setAttribute('aria-hidden', 'true');
this.accessibleMessage.innerText = message.innerText;
if (button) {
button.parentElement.removeChild(button);
}
listen () {
if (document.body.querySelector('.toc-cta')) {
this.button.classList.add('in-page-with-toc');
}
this.button.addEventListener('click', () => {
this.toggle(true);
this.removedItems = this.element.querySelector('.pagefind-ui__suppressed', '.pagefind-ui__search-clear');
if (this.removedItems) {
this.removedItems.remove();
}
});
this.closeButton.addEventListener('click', () => {
this.clearSearch();
this.toggle(false);
});
window.addEventListener('keydown', (event) => {
if (event.keyCode === 27 || event.key === 'Escape') {
if (this.state.isOpened) {
this.toggle(false);
this.button.focus();
}
} else if (event.key === 'Tab' && this.state.isOpened) {
focusTrap(event, this.element, this.state.isOpened);
}
this.buttonMore = this.element.querySelector('.pagefind-ui__results + button');
if (this.buttonMore && this.state.isOpened) {
this.buttonMore.addEventListener('click', () => {
this.buttonMoreFocus();
});
}
});
// if (this.a11yButton) {
// a11yClick(this.a11yButton, (event) => {
// if (isMobile()) {
// event.preventDefault();
// this.toggle(true);
// }
// });
// }
if (message) {
message.innerText = '';
}
clearSearch () {
const button = this.element.querySelector('.pagefind-ui__button'),
message = this.element.querySelector('.pagefind-ui__message'),
results = this.element.querySelector('.pagefind-ui__results');
if (results) {
results.innerHTML = '';
}
};
if (this.input) {
this.input.value = '';
this.searchInstance.triggerSearch(false);
}
window.osuny.Search.prototype.setAccessibility = function () {
this.input.setAttribute('title', this.input.getAttribute('placeholder'));
if (message) {
message.innerText = '';
}
if (results) {
results.innerHTML = '';
}
if (button) {
button.parentElement.removeChild(button);
}
}
// Add element to alert screen reader of the search results
this.accessibleMessageContainer = document.createElement('div');
this.element.append(this.accessibleMessageContainer);
this.accessibleMessageContainer.setAttribute('aria-live', 'polite');
this.accessibleMessageContainer.setAttribute('aria-atomic', 'true');
this.accessibleMessageContainer.classList.add('sr-only', 'pagefind-ui__accessible-message');
};
window.osuny.Search.prototype.updateAccessibility = function () {
this.updateAccessibilityMessage();
this.updateAccessibilityTags();
};
window.osuny.Search.prototype.updateAccessibilityTags = function () {
var results = this.element.querySelectorAll('.pagefind-ui__result');
results.forEach(this.updateAccessibilityResult);
};
buttonMoreFocus () {
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(addedNode => {
if (addedNode instanceof HTMLAnchorElement) {
addedNode.focus();
}
});
});
});
observer.observe(this.element, { childList: true, subtree: true });
window.osuny.Search.prototype.updateAccessibilityResult = function (result) {
var image = result.querySelector('img'),
title = result.querySelector('.pagefind-ui__result-title');
if (image) {
image.setAttribute('alt', '');
}
toggle(open = !this.state.isOpened) {
this.state.isOpened = open;
this.element.setAttribute('aria-hidden', !this.state.isOpened);
this.button.setAttribute('aria-expanded', this.state.isOpened);
const classAction = this.state.isOpened ? 'add' : 'remove';
document.documentElement.classList[classAction](CLASSES.modalOpened);
if (open) {
this.input = this.element.querySelector('input');
this.input.focus();
} else {
document.body.style.overflow = 'unset';
this.button.focus();
this.accessibleMessageContainer.innerHTML = '';
}
ariaHideBodyChildren(this.element, open);
if (title) {
title.setAttribute('role', 'heading');
title.setAttribute('aria-level', '2');
}
}
};
// Selectors
window.addEventListener('DOMContentLoaded', () => {
const pageFindContainer = document.querySelector('#search');
window.osuny.Search.prototype.updateAccessibilityMessage = function () {
var message = this.element.querySelector('.pagefind-ui__message');
if (!message) {
return;
}
message.setAttribute('aria-hidden', 'true');
if (typeof PagefindUI === 'undefined') return;
if (this.input.value !== '') {
this.accessibleMessage = document.createElement('p');
this.accessibleMessage.innerText = message.innerText;
this.accessibleMessageContainer.appendChild(this.accessibleMessage);
}
};
if (pageFindContainer) {
new Search(pageFindContainer);
// Selectors
window.addEventListener('DOMContentLoaded', function () {
var element = document.querySelector('#search');
if (typeof PagefindUI !== 'undefined' && element) {
window.osuny.search = new window.osuny.Search(element);
}
});
......@@ -13,11 +13,11 @@ import './design-system/mainMenu';
import './design-system/modal';
import './design-system/navAccessibility';
import './design-system/notes';
import './design-system/search';
import './design-system/toc';
import './design-system/Popup';
import './design-system/Lightbox';
import './design-system/sliders';
import './design-system/search';
import './blocks/keyFigures';
import './blocks/timeline';
......
......@@ -134,4 +134,12 @@ $toc-overlay-color: $body-overlay-color !default
$table-head-font-size: $h4-size !default
$table-head-font-size-desktop: $h4-size-desktop !default
$table-body-size: $body-size !default
$table-body-size-desktop: $body-size-desktop !default
\ No newline at end of file
$table-body-size-desktop: $body-size-desktop !default
// Search
$search-button-header-icon: true
$search-button-header-text: false
$search-button-footer-icon: true
$search-button-footer-text: true
$search-button-fixed-icon: true
$search-button-fixed-text: true
\ No newline at end of file
......@@ -3,11 +3,12 @@ $zindex-aside: 48 !default
$zindex-body-overlay: 51 !default
$zindex-dropdown: 3 !default
$zindex-footer: 50 !default
$zindex-header: 52 !default
$zindex-header: 55 !default
$zindex-lightbox: 80 !default
$zindex-modal: 72 !default
$zindex-nav-accessibility: 1010 !default
$zindex-toc: 50 !default
$zindex-toc-cta: 49 !default
$zindex-toc-offcanvas: 60 !default
$zindex-search-button-fixed: 51 !default
$zindex-stretched-link: 2 !default
\ No newline at end of file
......@@ -27,6 +27,8 @@ footer#document-footer
+ .container,
> * + *
margin-top: $spacing-5
+ .footer-search
margin-top: 0
@include media-breakpoint-up(desktop)
.container
......
......@@ -4,7 +4,6 @@ header#document-header
color: $header-color
position: fixed
left: 0
transition: transform $header-sticky-transition, background $header-sticky-transition
top: 0
width: 100%
z-index: $zindex-header
......@@ -32,9 +31,9 @@ header#document-header
background: $header-sticky-dropdown-background
color: $header-sticky-dropdown-color
&.is-sticky
.pagefind-ui__toggle
.search-button
color: $header-sticky-color
html.is-scrolling-down:not(.has-menu-opened, .has-modal-opened, .has-search-opened) &
html.is-scrolling-down:not(.has-menu-opened, .has-modal-opened) &
@include media-breakpoint-down(desktop)
transform: translateY(-100%)
@include media-breakpoint-up(desktop)
......@@ -95,28 +94,27 @@ header#document-header
.nav-level-1
a
text-decoration: none
> li:not(.nav-search)
> li
line-height: 1
border: none
display: flex
align-items: center
@include media-breakpoint-down(desktop)
height: 100%
a,
span,
button
> a,
> span
display: block
padding: 0
white-space: nowrap
line-height: 1
> *
@include media-breakpoint-up(desktop)
padding: $header-upper-menu-padding-y 0
@include media-breakpoint-down(desktop)
line-height: $header-upper-menu-mobile-height
// apply active style to active page (default) or active site
$upper-menu-active-selector: '.active'
@if $header-upper-menu-active-style-for-sites
$upper-menu-active-selector: ':not([href*="https://"], [href*="http://"])'
&#{$upper-menu-active-selector}
box-shadow: $header-upper-menu-active-box-shadow
// apply active style to active page (default) or active site
$upper-menu-active-selector: '.active'
@if $header-upper-menu-active-style-for-sites
$upper-menu-active-selector: ':not([href*="https://"], [href*="http://"])'
a#{$upper-menu-active-selector}
box-shadow: $header-upper-menu-active-box-shadow
@include media-breakpoint-down(desktop)
&.has-upper-menu
.menu
......@@ -166,7 +164,7 @@ body
opacity: 0
header#document-header
nav:not(.upper-menu)
nav.primary-menu
padding-bottom: $header-nav-padding-y
padding-top: $header-nav-padding-y
@include media-breakpoint-up(desktop)
......
......@@ -140,7 +140,7 @@
@include media-breakpoint-up(desktop)
.nav-level-1
align-items: center
align-items: baseline
.nav-level-3
li
margin-top: $spacing-2
......@@ -209,18 +209,18 @@
text-decoration: none
.nav-level-1
display: block
li
a, span
li
> a, > span
padding: $spacing-1 0
display: block
> li:not(:last-child)
border-bottom: 1px solid $color-border
> li
> a, > span
> a, > span, > button
padding-bottom: $spacing-3
padding-top: $spacing-3
li.has-children
[role="button"]
[role="button"], button
align-items: center
display: flex
justify-content: space-between
......
.pagefind-ui__toggle,
.pagefind-ui__button,
.search-button,
.pagefind-ui__button,
.search__close
@include button-reset
@include meta
&:focus
box-shadow: none
.pagefind-ui__toggle:not(.pagefind-primary),
.pagefind-ui__button,
.search__close
@include meta
color: var(--color-text)
.pagefind-ui__toggle
color: $header-color
cursor: pointer
text-align: left
box-shadow: none
&:not(.toggle-text)
@include icon(search-line, after)
&::after
line-height: inherit
margin-left: $spacing-1
&:not(.pagefind-primary)
padding: $spacing-1 $spacing-2
&.toggle-both
.menu & span
padding-right: 0
span,
&::after
display: inline
@include media-breakpoint-down(desktop)
&.pagefind-primary
padding: $spacing-3 0
width: 100%
@include media-breakpoint-up(desktop)
&.toggle-icon::after
margin-left: 0
.upper-menu &
&.toggle-both,
&.toggle-text
white-space: nowrap
padding: 0
@include media-breakpoint-down(desktop)
&.toggle-both::after
margin-left: space()
.search__close
@include icon-block(close-line, after)
position: fixed
......@@ -58,9 +22,11 @@
@include media-breakpoint-down(desktop)
right: $spacing-3
top: $spacing-1
#search
background: var(--color-background)
color: var(--color-text)
display: none
height: 100vh
left: 0
padding-bottom: $spacing-5
......@@ -74,11 +40,12 @@
z-index: 80
@include media-breakpoint-up(desktop)
padding-top: $spacing-4
&[aria-hidden=true]
display: none
&.is-opened
display: block
a
color: var(--color-text)
.pagefind-ui
// Used to hide results behind search input
&::before
content: ""
background: var(--color-background)
......@@ -187,66 +154,69 @@
&__results-area
padding-top: space(17)
.menu .nav-level-1 > li .pagefind-ui__toggle:not(.toggle-icon)::after
margin-left: 0
margin-top: pxToRem(2)
// Toggle button appareance
.search-button
align-items: baseline
cursor: pointer
display: flex
gap: $spacing-1
padding: 0
text-align: left
white-space: nowrap
line-height: 1
.pagefind-fixed
// Can be with icon only, text only, or both
.search-button
align-items: center
background: var(--color-background-alt)
bottom: 0
color: var(--color-text)
display: flex
gap: $spacing-1
justify-content: space-between
left: var(--grid-gutter)
position: fixed
z-index: 10
@include media-breakpoint-up(desktop)
&:not(.toggle-icon)
min-width: columns(2)
&.toggle-icon:not(:hover)
padding-left: 0
padding-right: 0
justify-content: center
&.toggle-icon
min-height: pxToRem(37)
width: pxToRem(50)
text-align: left
white-space: nowrap
line-height: 1
&::after
line-height: inherit
&--primary, &--upper-menu
align-items: baseline
color: $header-color
@if not $search-button-header-text
span
display: none
&:hover,
&:focus
min-width: columns(2)
span
display: inline
@include media-breakpoint-down(desktop)
left: 0
width: 100vw
&.in-page-with-toc
background: var(--color-background)
border-top: 1px solid #eee
bottom: pxToRem(44)
footer#document-footer .footer-search
.pagefind-footer
@extend .sr-only
@if $search-button-header-icon
@include icon(search-line, after)
&--primary
width: 100%
&--footer
color: $footer-color
padding: 0
&.toggle-icon
transform: translateX(-35%)
&::after
margin-top: pxToRem(3)
#search
margin-top: 0
a
color: var(--color-text)
.nav-search
display: flex
.pagefind-primary:not(.toggle-icon)::after
margin-top: 10px
.menu .nav-level-1 > li .pagefind-ui__toggle span
display: inline
@include media-breakpoint-up(desktop)
.nav-level-1
> li .pagefind-ui__toggle.toggle-icon span
display: none
\ No newline at end of file
@if not $search-button-footer-text
span
@extend .sr-only
@if $search-button-footer-icon
@include icon(search-line, after)
&--fixed
background: var(--color-background-alt)
bottom: 0
color: var(--color-text)
left: var(--grid-gutter)
min-height: $minimum-accessible-size
padding: $spacing-1 $spacing-2
position: fixed
z-index: $zindex-search-button-fixed
@if not $search-button-fixed-text
span
@extend .sr-only
@if $search-button-fixed-icon
@include icon(search-line, after)
@include media-breakpoint-down(desktop)
left: 0
width: 100vw
&.in-page-with-toc
background: var(--color-background)
border-top: 1px solid #eee
bottom: pxToRem(44)
padding: 0 var(--grid-gutter)
@include media-breakpoint-up(desktop)
@if $search-button-fixed-text
width: columns(2)
......@@ -57,8 +57,11 @@ params:
position: hero # hero | content
search:
active: false
position: primary # primary | upper-menu | fixed | footer
appearance: icon # icon | text | both
positions:
- primary
# - upper-menu
# - fixed
# - footer
home:
head:
title: both # both | page | site
......
......@@ -16,19 +16,13 @@
{{- partial "hooks/before-header" . -}}
{{- partial "header/header.html" . -}}
<main{{ if .Params.contents }} class="page-with-blocks"{{ end }} id="main">
{{ if and (site.Params.search.active) (eq site.Params.search.position "fixed")}}
{{ partial "header/search.html"
(dict
"position" "fixed"
"context" .
)
}}
{{ end }}
{{ partial "commons/search/button.html" "fixed" }}
{{- block "main" . }}{{- end }}
{{- partial "hooks/before-main-end" . -}}
</main>
{{- partial "footer/footer.html" . -}}
{{- partial "commons/lightbox/lightbox.html" . -}}
{{- partial "commons/search/modal.html" . -}}
{{- partial "footer/plausible.html" . -}}
{{- partial "footer/js.html" . -}}
{{- partial "footer/script.html" . -}}
......
......@@ -98,12 +98,9 @@
{{- end -}}
{{ end -}}
{{ if and site.Params.search.active (eq site.Params.search.position $kind) }}
{{ if and site.Params.search.active (in site.Params.search.positions $kind) }}
<li class="nav-search">
{{ partial "header/search.html" (dict
"position" $kind
"context" .
)}}
{{ partial "commons/search/button.html" $kind }}
</li>
{{ end }}
{{ if .i18n }}
......
{{ $position := . }}
{{ if and site.Params.search.active (in site.Params.search.positions $position) }}
<button
class="search-button search-button--{{ $position }}"
aria-expanded="false"
aria-label='{{- i18n (printf "commons.search.title") -}}'>
<span>{{- i18n (printf "commons.search.title") -}}</span>
</button>
{{ end }}
\ No newline at end of file
{{ if site.Params.search.active }}
<script src="/pagefind/pagefind-ui.js"></script>
<div id="search" class="search" aria-hidden="true" aria-modal="true" role="dialog" aria-label="{{ i18n "commons.search.label" }}">
<button class="search__close popup-close" aria-label="{{ i18n "commons.search.close" }}">
{{ i18n "commons.search.close" }}
</button>
<p role="heading" aria-level="1" class="sr-only">{{ i18n "commons.search.title" }}</p>
</div>
{{ end }}
\ No newline at end of file
......@@ -7,16 +7,13 @@
</div>
</div>
{{ partial "footer/i18n.html" . }}
{{ if and (site.Params.search.active) (eq site.Params.search.position "footer")}}
{{ if in site.Params.search.positions "footer" }}
<div class="container footer-search">
{{ partial "header/search.html"
(dict
"position" "footer"
"context" .
)
}}
{{ partial "commons/search/button.html" "footer" }}
</div>
{{ end }}
<div class="container">
<div class="footer-social">
{{ partial "footer/social.html" }}
......
......@@ -18,7 +18,7 @@
</div>
</nav>
{{ end }}
<nav aria-label="{{ i18n "commons.menu.main" }}">
<nav class="primary-menu" aria-label="{{ i18n "commons.menu.main" }}">
<div class="container">
{{ partial "header/logo.html" }}
{{ if $primary.items }}
......
{{ $search_appareance := site.Params.search.appearance }}
{{ $search_position := site.Params.search.position }}
<button id="search-button" class="pagefind-ui__toggle pagefind-{{ .position }} toggle-{{ $search_appareance }}" aria-expanded="false" aria-label='{{- i18n (printf "commons.search.title") -}}'>
<span>{{- i18n (printf "commons.search.title") -}}</span>
</button>
<script src="/pagefind/pagefind-ui.js"></script>
<div id="search" class="search__modal" aria-hidden="true" aria-modal="true" role="dialog" aria-label="{{ i18n "commons.search.label" }}">
<button class="search__close" aria-label="{{ i18n "commons.search.close" }}">
{{ i18n "commons.search.close" }}
</button>
<p role="heading" aria-level="1" class="sr-only">{{ i18n "commons.search.title" }}</p>
</div>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment