Commit 9630f296 authored by catch's avatar catch
Browse files

Issue #3051766 by bnjmnm, alexpott, Oleksiy, finnsky, zrpnr, jungle, Wim...

Issue #3051766 by bnjmnm, alexpott, Oleksiy, finnsky, zrpnr, jungle, Wim Leers, vacho, anmolgoyal74, lauriii, tim.plunkett, rachel_norfolk, andypost, catch, Gábor Hojtsy, nod_, fubarhouse: Deprecate and replace jQuery Joyride (for tours)
parent 8db131bd
......@@ -19,6 +19,7 @@
"Backbone": true,
"Modernizr": true,
"Popper": true,
"Shepherd": true,
"Sortable": true,
"once": true,
"CKEDITOR": true,
......
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -503,9 +503,20 @@ jquery.joyride:
gpl-compatible: true
js:
assets/vendor/jquery-joyride/jquery.joyride-2.1.js: { }
deprecated: The "%library_id%" asset library is deprecated in drupal:9.2.0 and is removed from drupal:10.0.0. Use the core/shepherd library instead. See https://www.drupal.org/node/3195234
dependencies:
- core/jquery
shepherd:
remote: https://github.com/shipshapecode/shepherd
version: "8.3.1"
license:
name: MIT
url: https://raw.githubusercontent.com/shipshapecode/shepherd/v8.3.1/LICENSE
gpl-compatible: true
js:
assets/vendor/shepherd/shepherd.min.js: { minified: true }
jquery.once:
remote: https://github.com/RobLoach/jquery-once
version: "2.2.3"
......
......@@ -21,24 +21,21 @@ tips:
label: 'Place Blocks'
body: 'Any custom or contributed block can be added to a particular region by clicking on a button Place block. A new block can also be created by clicking on Place Block'
weight: 2
attributes:
data-class: button--small
selector: '.button--small'
block-region:
id: block-region
plugin: text
label: 'Block Region'
body: 'Assign or change the region of a block by clicking here. A dropdown list with all the regions will appear.You can place one block in multiple regions.'
weight: 3
attributes:
data-class: block-region-select
selector: '.block-region-select'
configure-block:
id: configure-block
plugin: text
label: 'Configure Block'
body: 'By Clicking on "Configure" you can go ahead and edit the contents of the block, deal with the visibility settings and even change the placement of where it is on your theme.'
weight: 4
attributes:
data-class: dropbutton-widget
selector: '.dropbutton-widget'
custom-block-library:
id: custom-block-library
plugin: text
......
......@@ -67,9 +67,7 @@ protected function createEntity() {
'label' => 'Llama',
'body' => 'Who handle the awesomeness of llamas?',
'weight' => 100,
'attributes' => [
'data-id' => 'tour-llama-1',
],
'selector' => '#tour-llama-1',
],
],
]);
......@@ -119,9 +117,7 @@ protected function getExpectedDocument() {
'label' => 'Llama',
'body' => 'Who handle the awesomeness of llamas?',
'weight' => 100,
'attributes' => [
'data-id' => 'tour-llama-1',
],
'selector' => '#tour-llama-1',
],
],
'drupal_internal__id' => 'tour-llama',
......
......@@ -21,8 +21,7 @@ tips:
label: 'Select language'
body: '<p>Choose a language from the list, or choose "Custom language..." at the end of the list.</p><p>Click the "Add language" button when you are done choosing your language.</p><p>When adding a custom language, you will get an additional form where you can provide the name, code, and direction of the language.</p>'
weight: 2
attributes:
data-id: edit-predefined-langcode
selector: '#edit-predefined-langcode'
language-add-continue:
id: language-add-continue
plugin: text
......
......@@ -21,24 +21,21 @@ tips:
label: 'Language code'
body: '<p>You cannot change the code of a language on the site, since it is used by the system to keep track of the language.</p>'
weight: 2
attributes:
data-id: edit-langcode-view
selector: '#edit-langcode-view'
language-edit-label:
id: language-edit-label
plugin: text
label: 'Language name'
body: '<p>The language name is used throughout the site for all users and is written in English. Names of built-in languages can be translated using the Interface Translation module, and names of both built-in and custom languages can be translated using the Configuration Translation module.</p>'
weight: 3
attributes:
data-id: edit-label
selector: '#edit-label'
language-edit-direction:
id: language-edit-direction
plugin: text
label: 'Language direction'
body: '<p>Choose if the language is a "Left to right" or "Right to left" language.</p><p>Note that not all themes support "Right to left" layouts, so test your theme if you are using "Right to left".</p>'
weight: 4
attributes:
data-id: edit-direction--wrapper--description
selector: '#edit-direction--wrapper--description'
language-edit-continue:
id: language-edit-continue
plugin: text
......
......@@ -21,32 +21,28 @@ tips:
label: 'Adding languages'
body: '<p>To add more languages to your site, click the "Add language" button.</p><p>Added languages will be displayed in the language list and can then be edited or deleted.</p>'
weight: 2
attributes:
data-class: button-action
selector: '.button-action'
language-reorder:
id: language-reorder
plugin: text
label: 'Reordering languages'
body: '<p>To reorder the languages on your site, use the drag icons next to each language.</p><p>The order shown here is the display order for language lists on the site such as in the language switcher blocks provided by the Interface Translation and Content Translation modules.</p><p>When you are done with reordering the languages, click the "Save configuration" button for the changes to take effect.</p>'
weight: 3
attributes:
data-class: draggable
selector: '.draggable'
language-default:
id: language-default
plugin: text
label: 'Set a language as default'
body: '<p>You can change the default language of the site by choosing one of your configured languages as default. The site will use the default language in situations where no choice is made but a language should be set, for example as the language of the displayed interface.</p>'
weight: 4
attributes:
data-class: js-form-item-site-default-language
selector: '.js-form-item-site-default-language'
language-operations:
id: language-operations
plugin: text
label: 'Modifying languages'
body: '<p>Operations are provided for editing and deleting your languages.</p><p>You can edit the name and the direction of the language.</p><p>Deleted languages can be added back at a later time. Deleting a language will remove all interface translations associated with it, and content in this language will be set to be language neutral. Note that you cannot delete the default language of the site.</p>'
weight: 5
attributes:
data-class: dropbutton-wrapper
selector: '.dropbutton-wrapper'
language-continue:
id: language-continue
plugin: text
......
......@@ -21,48 +21,42 @@ tips:
label: 'Translation language'
body: 'Choose the language you want to translate.'
weight: 2
attributes:
data-id: edit-langcode
selector: '#edit-langcode'
locale-search:
id: locale-search
plugin: text
label: Search
body: 'Enter the specific word or sentence you want to translate, you can also write just a part of a word.'
weight: 3
attributes:
data-id: edit-string
selector: '#edit-string'
locale-filter:
id: locale-filter
plugin: text
label: 'Filter the search'
body: 'You can search for untranslated strings if you want to translate something that isn''t translated yet. If you want to modify an existing translation, you might want to search only for translated strings.'
weight: 4
attributes:
data-id: edit-translation
selector: '#edit-translation'
locale-submit:
id: locale-submit
plugin: text
label: 'Apply your search criteria'
body: 'To apply your search criteria, click on the <em>Filter</em> button.'
weight: 5
attributes:
data-id: edit-submit
selector: '#edit-submit'
locale-translate:
id: locale-translate
plugin: text
label: Translate
body: 'You can write your own translation in the text fields of the right column. Try to figure out in which context the text will be used in order to translate it in the appropriate way.'
weight: 6
attributes:
data-class: js-form-type-textarea
selector: '.js-form-type-textarea'
locale-validate:
id: locale-validate
plugin: text
label: 'Validate the translation'
body: 'When you have finished your translations, click on the <em>Save translations</em> button. You must save your translations, each time before changing the page or making a new search.'
weight: 7
attributes:
data-id: edit-submit--2
selector: '#edit-submit--2'
locale-continue:
id: locale-continue
plugin: text
......
......@@ -43,9 +43,17 @@ tour.tip:
type: integer
label: 'Weight'
location:
deprecated: "The tour.tip 'location' config schema property is deprecated in drupal:9.2.0 and is removed from drupal:10.0.0. Instead use 'position'. The value must be a valid placement accepted by PopperJS. See https://www.drupal.org/node/3204093"
type: string
label: 'Location'
position:
type: string
label: 'Position'
selector:
type: string
label: 'Selector'
attributes:
deprecated: "The tour.tip 'attributes' config schema property is deprecated in drupal:9.2.0 and is removed from drupal:10.0.0. Instead of 'data-class' and 'data-id' attributes, use 'selector' to specify the element a tip attaches to. See https://www.drupal.org/node/3204093"
type: sequence
label: 'Attributes'
sequence:
......
......@@ -11,7 +11,7 @@
float: left;
}
/* Override placement of the tour progress indicator. */
/* Style the tour progress indicator. */
.tour-progress {
position: absolute;
right: 20px; /* LTR */
......@@ -22,122 +22,132 @@
left: 20px;
}
/* Default styles for the container */
.joyride-tip-guide {
/**
* The following are largely Shepherd's default styles, with a few modifications
* to facilitate a graceful transition from Joyride, the library used prior to
* Shepherd.
*/
.shepherd-footer {
display: flex;
justify-content: flex-start;
padding: 0 20px 20px;
}
.shepherd-footer .shepherd-button:last-child {
margin-right: 0;
}
.shepherd-cancel-icon {
position: absolute;
top: 20px;
right: 20px;
margin: 0;
padding: 0;
cursor: pointer;
border: none;
background: transparent;
line-height: 1em;
}
.shepherd-title {
margin: 0;
padding: 0;
}
.shepherd-header {
position: relative;
margin-bottom: 10px;
padding: 20px 50px 0 20px;
}
.shepherd-text {
padding: 0 20px;
}
.shepherd-text p {
margin: 0 0 1.4em;
}
.shepherd-element {
z-index: 101;
top: 0;
left: 0;
display: none;
width: 300px;
background: #fff;
}
@media only screen and (max-width: 767px) {
.joyride-tip-guide {
.shepherd-element {
left: 2.5%;
width: 85%;
}
}
.joyride-content-wrapper {
position: relative;
padding: 20px 50px 20px 20px; /* LTR */
}
[dir="rtl"] .joyride-content-wrapper {
padding: 20px 20px 20px 50px;
}
/* Add a little css triangle pip, older browser just miss out on the fanciness of it. */
.joyride-tip-guide .joyride-nub {
position: absolute;
left: 22px;
display: block;
width: 0;
height: 0;
.shepherd-enabled.shepherd-element {
opacity: 1;
}
.joyride-tip-guide .joyride-nub.top {
top: -28px;
bottom: auto;
.shepherd-element[data-popper-reference-hidden]:not(.shepherd-centered) {
opacity: 0;
}
.joyride-tip-guide .joyride-nub.bottom {
bottom: -28px;
.shepherd-element,
.shepherd-element *,
.shepherd-element :after,
.shepherd-element :before {
box-sizing: border-box;
}
.joyride-tip-guide .joyride-nub.right {
top: 22px;
right: -28px;
bottom: auto;
left: auto;
.shepherd-arrow,
.shepherd-arrow:before {
position: absolute;
width: 16px;
height: 16px;
}
.joyride-tip-guide .joyride-nub.left {
top: 22px;
right: auto;
bottom: auto;
left: -28px;
.shepherd-arrow:before {
content: "";
transform: rotate(45deg);
background: #fff;
}
.joyride-tip-guide .joyride-nub.top-right {
top: -28px;
right: 28px;
bottom: auto;
left: auto;
.shepherd-element[data-popper-placement^=top] > .shepherd-arrow {
bottom: -8px;
}
.joyride-tip-guide .tour-tip-label {
margin-top: 0;
.shepherd-element[data-popper-placement^=bottom] > .shepherd-arrow {
top: -8px;
}
.joyride-tip-guide p {
margin: 0 0 1.4em;
.shepherd-element[data-popper-placement^=left] > .shepherd-arrow {
right: -8px;
}
.joyride-timer-indicator-wrap {
position: absolute;
right: 17px;
bottom: 16px;
width: 50px;
height: 3px;
}
.joyride-timer-indicator {
display: block;
width: 0;
height: inherit;
.shepherd-element[data-popper-placement^=right] > .shepherd-arrow {
left: -8px;
}
.joyride-close-tip {
position: absolute;
top: 20px;
right: 20px; /* LTR */
line-height: 1em;
}
[dir="rtl"] .joyride-close-tip {
right: auto;
left: 20px;
.shepherd-target-click-disabled.shepherd-enabled.shepherd-target,
.shepherd-target-click-disabled.shepherd-enabled.shepherd-target * {
pointer-events: none;
}
.joyride-modal-bg {
.shepherd-modal-overlay-container {
position: fixed;
z-index: 100;
top: 0;
left: 0;
display: none;
width: 100%;
height: 100%;
cursor: pointer;
overflow: hidden;
width: 100vw;
height: 0;
pointer-events: none;
opacity: 0;
fill-rule: evenodd;
}
.joyride-expose-wrapper {
position: absolute;
z-index: 102;
.shepherd-modal-overlay-container.shepherd-modal-is-visible {
height: 100vh;
opacity: 0.5;
}
.joyride-expose-cover {
position: absolute;
z-index: 10000;
top: 0;
left: 0;
.shepherd-modal-overlay-container.shepherd-modal-is-visible path {
pointer-events: all;
}
......@@ -3,7 +3,7 @@
* Attaches behaviors for the Tour module's toolbar tab.
*/
(function ($, Backbone, Drupal, document) {
(($, Backbone, Drupal, settings, document, Shepherd) => {
const queryString = decodeURI(window.location.search);
/**
......@@ -29,6 +29,7 @@
.once('tour')
.each(() => {
const model = new Drupal.tour.models.StateModel();
// eslint-disable-next-line no-new
new Drupal.tour.views.ToggleTourView({
el: $(context).find('#toolbar-tab-tour'),
model,
......@@ -36,14 +37,16 @@
model
// Allow other scripts to respond to tour events.
.on('change:isActive', (model, isActive) => {
.on('change:isActive', (tourModel, isActive) => {
$(document).trigger(
isActive ? 'drupalTourStarted' : 'drupalTourStopped',
);
})
// Initialization: check whether a tour is available on the current
// page.
.set('tour', $(context).find('ol#tour'));
});
// Initialization: check whether a tour is available on the current
// page.
if (settings._tour_internal) {
model.set('tour', settings._tour_internal);
}
// Start the tour immediately if toggled via query string.
if (/tour=?/i.test(queryString)) {
......@@ -138,7 +141,7 @@
this.$el
.find('button')
.toggleClass('is-active', isActive)
.prop('aria-pressed', isActive);
.attr('aria-pressed', isActive);
return this;
},
......@@ -147,27 +150,105 @@
*/
toggleTour() {
if (this.model.get('isActive')) {
const $tour = this._getTour();
this._removeIrrelevantTourItems($tour, this._getDocument());
this._removeIrrelevantTourItems(this._getTour());
const tourItems = this.model.get('tour');
const that = this;
const close = Drupal.t('Close');
if ($tour.find('li').length) {
$tour.joyride({
autoStart: true,
postRideCallback() {
that.model.set('isActive', false);
},
// HTML segments for tip layout.
template: {
link: `<a href="#close" class="joyride-close-tip" aria-label="${close}">&times;</a>`,
button:
'<a href="#" class="button button--primary joyride-next-tip"></a>',
if (tourItems.length) {
// If Joyride is positioned relative to the top or bottom of an
// element, and its secondary position is right or left, then the
// arrow is also positioned right or left. Shepherd defaults to
// center positioning the arrow.
//
// In most cases, this arrow positioning difference has
// little impact. However, tours built with Joyride may have tips
// using a higher level selector than the element the tip is
// expected to point to, and relied on Joyride's arrow positioning
// to align the arrow with the expected reference element. Joyride's
// arrow positioning behavior is replicated here to prevent those
// use cases from causing UI regressions.
//
// This modifier is provided here instead of TourViewBuilder (where
// most position modifications are) because it includes adding a
// JavaScript callback function.
settings.tourShepherdConfig.defaultStepOptions.popperOptions.modifiers.push(
{
name: 'moveArrowJoyridePosition',
enabled: true,
phase: 'write',
fn({ state }) {
const { arrow } = state.elements;
const { placement } = state;
if (
arrow &&
/^top|bottom/.test(placement) &&
/-start|-end$/.test(placement)
) {
const horizontalPosition = placement.split('-')[1];
const offset =
horizontalPosition === 'start'
? 28
: state.elements.popper.clientWidth - 56;
arrow.style.transform = `translate3d(${offset}px, 0px, 0px)`;
}
},
},
);
const shepherdTour = new Shepherd.Tour(settings.tourShepherdConfig);
shepherdTour.on('cancel', () => {
that.model.set('isActive', false);
});
shepherdTour.on('complete', () => {
that.model.set('isActive', false);
});
tourItems.forEach((tourStepConfig, index) => {
// Create the configuration for a given tour step by using values
// defined in TourViewBuilder.
// @see \Drupal\tour\TourViewBuilder::viewMultiple()
const tourItemOptions = {
title: tourStepConfig.title
? Drupal.checkPlain(tourStepConfig.title)
: null,
text: () => Drupal.theme('tourItemContent', tourStepConfig),
attachTo: tourStepConfig.attachTo,
buttons: [Drupal.tour.nextButton(shepherdTour, tourStepConfig)],
classes: tourStepConfig.classes,
index,
};