Commit 27881b60 authored by Benjamin Franzke's avatar Benjamin Franzke Committed by Benni Mack
Browse files

[TASK] Update bootstrap javascript to 5.0.0-beta1

Bootstrap v5 – introduced in #92616 – was added with CCS from beta1 but
JavaScript from alpha2. bootstrap.bundle.js was manually wrapped
into a AMD closure, and because bootstrap 5.0.0-beta1 contains alot of
changes regarding data tags, it couldn't be updated in the initial
patch.

Bootstrap is now bundled using rollup using the ES6 sources in order
to allow for automatic updates through `grunt build`.

popperjs – previously bundled into bootstrap distributed files –
is now added as dependency. The bootstap ES6 sources, that we now use
through rollup, do not bundle this external dependency (for good reasons).

Dependency added with:

   yarn add @popperjs/core

Further adaptions contained in this change to ensure beta1 compatibility:

a) Carousel "item" to "carousel-item" class migration
b) $.fn.modal(options) does no longer imply $.fn.modal('show')
c) Fix panels, both JS and CSS (card-group can't be used here)
d) All bootstrap data- tags are migrated to data-bs-.
   (see https://github.com/twbs/bootstrap/pull/31827)
   Migrated with

   # renderes a sed substition with the help of a nested sed from all the
   # data-bs attributes that where changed in the twbs/bootstrap commit
   git grep -l data- | xargs sed -i $( \
        curl -s \
        https://patch-diff.githubusercontent.com/raw/twbs/bootstrap/pull/31827.patch | \
        sed 's/data-bs-[a-z-]*/\n&\n/g' | grep "data-bs-[a-z-]" | \
        sort | uniq | \
        sed 's/data-bs-\(.*\)\([^a-z-]\|$\)/ -e s\/data-\1\\\([^a-z-]\\\)\/data-bs-\1\\1\/g -e s\/data('"'"'\1'"'"')\/data('"'"'bs-\1'"'"')\/g/g' \
   )

   # Revert false positives from the above auto-replacement
   git checkout -- typo3/sysext/core/Documentation/Changelog/ \
        typo3/sysext/backend/Classes/Form/Container/FlexFormContainerContainer.php \
        Build/Sources/TypeScript/backend/Resources/Public/TypeScript/LiveSearch.ts \
        Build/Sources/TypeScript/backend/Resources/Public/TypeScript/FormEngineFlexForm.ts \
        Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/ExtensionConfiguration.ts \
        Build/Sources/Sass/typo3/_element_panel.scss

   (cd Build && grunt build)

Resolves: #93126
Resolves: #93123
Resolves: #93132
Related: #92616
Releases: master
Change-Id: Ie194d0f87d2c60df7b9e8a6de4893cfaaea55356
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/67215

Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: default avatarMartin Kutschker <mkutschker-typo3@yahoo.com>
Tested-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Reviewed-by: default avatarMartin Kutschker <mkutschker-typo3@yahoo.com>
Reviewed-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
parent 89f2e9f8
......@@ -127,7 +127,7 @@ class Wizard {
this.initializeEvents();
this.getComponent().on('wizard-visible', (): void => {
this.runSlideCallback(firstSlide, this.setup.$carousel.find('.item').first());
this.runSlideCallback(firstSlide, this.setup.$carousel.find('.carousel-item').first());
}).on('wizard-dismissed', (): void => {
this.setup = $.extend(true, {}, this.originalSetup);
});
......@@ -229,7 +229,7 @@ class Wizard {
}
private addProgressBar(): void {
let realSlideCount = this.setup.$carousel.find('.item').length;
let realSlideCount = this.setup.$carousel.find('.carousel-item').length;
let slideCount = Math.max(1, realSlideCount);
let initialStep;
let $modal = this.setup.$carousel.closest('.modal');
......@@ -270,7 +270,7 @@ class Wizard {
return this.setup.$carousel;
}
let slides = '<div class="carousel slide" data-ride="carousel" data-interval="false">'
let slides = '<div class="carousel slide" data-bs-ride="carousel" data-bs-interval="false">'
+ '<div class="carousel-inner" role="listbox">';
for (let currentSlide of Object.values(this.setup.slides)) {
......@@ -279,13 +279,13 @@ class Wizard {
if (typeof slideContent === 'object') {
slideContent = slideContent.html();
}
slides += '<div class="item" data-slide="' + currentSlide.identifier + '">' + slideContent + '</div>';
slides += '<div class="carousel-item" data-bs-slide="' + currentSlide.identifier + '">' + slideContent + '</div>';
}
slides += '</div></div>';
this.setup.$carousel = $(slides);
this.setup.$carousel.find('.item').first().addClass('active');
this.setup.$carousel.find('.carousel-item').first().addClass('active');
return this.setup.$carousel;
}
......
......@@ -101,7 +101,7 @@ class LanguagePacks extends AbstractInteractableModule {
contentContainer.empty();
contentContainer.append(this.languageMatrixHtml(data));
contentContainer.append(this.extensionMatrixHtml(data));
$('[data-toggle="tooltip"]').tooltip(<any>({container: contentContainer}));
$('[data-bs-toggle="tooltip"]').tooltip(<any>({container: contentContainer}));
} else {
const message = InfoBox.render(Severity.error, 'Something went wrong', '');
this.addNotification(message);
......@@ -337,13 +337,13 @@ class LanguagePacks extends AbstractInteractableModule {
$('<a>', {
'class': 'btn btn-default t3js-languagePacks-deactivateLanguage',
'data-iso': language.iso,
'data-toggle': 'tooltip',
'data-bs-toggle': 'tooltip',
'title': 'Deactivate',
}).append(deactivateIcon),
$('<a>', {
'class': 'btn btn-default t3js-languagePacks-update',
'data-iso': language.iso,
'data-toggle': 'tooltip',
'data-bs-toggle': 'tooltip',
'title': 'Download language packs',
}).append(updateIcon),
),
......@@ -358,7 +358,7 @@ class LanguagePacks extends AbstractInteractableModule {
$('<a>', {
'class': 'btn btn-default t3js-languagePacks-activateLanguage',
'data-iso': language.iso,
'data-toggle': 'tooltip',
'data-bs-toggle': 'tooltip',
'title': 'Activate',
}).append(activateIcon),
),
......@@ -428,7 +428,7 @@ class LanguagePacks extends AbstractInteractableModule {
$('<a>', {
'class': 'btn btn-default t3js-languagePacks-update',
'data-iso': language,
'data-toggle': 'tooltip',
'data-bs-toggle': 'tooltip',
'title': 'Download and update all language packs',
}).append(
$('<span>').append(updateIcon),
......@@ -479,7 +479,7 @@ class LanguagePacks extends AbstractInteractableModule {
'class': 'btn btn-default t3js-languagePacks-update',
'data-extension': extension.key,
'data-iso': pack.iso,
'data-toggle': 'tooltip',
'data-bs-toggle': 'tooltip',
'title': securityUtility.encodeHtml(tooltip),
}).append(updateIcon),
);
......
......@@ -241,7 +241,7 @@ class Router {
+ '<div class="panel panel-default panel-flat searchhit">'
+ '<div class="panel-heading" role="tab" id="heading-error">'
+ '<h3 class="panel-title">'
+ '<a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapse-error" aria-expanded="true" '
+ '<a role="button" data-bs-toggle="collapse" data-bs-parent="#accordion" href="#collapse-error" aria-expanded="true" '
+ 'aria-controls="collapse-error" class="collapsed">'
+ '<span class="caret"></span>'
+ '<strong>Ajax error</strong>'
......
......@@ -68,7 +68,7 @@ class Recordlist {
const $me = $(e.currentTarget);
const table = $me.data('table');
const $target = $($me.data('target'));
const $target = $($me.data('bs-target'));
const isExpanded = $target.data('state') === 'expanded';
const $collapseIcon = $me.find('.collapseIcon');
const toggleIcon = isExpanded ? this.identifier.icons.expand : this.identifier.icons.collapse;
......
......@@ -293,7 +293,7 @@ class Backend extends Workspaces {
}).on('click', '[data-action="remove"]', this.confirmDeleteRecordFromWorkspace)
.on('click', '[data-action="expand"]', (e: JQueryEventObject): void => {
const $me = $(e.currentTarget);
const $target = this.elements.$tableBody.find($me.data('target'));
const $target = this.elements.$tableBody.find($me.data('bs-target'));
let iconIdentifier;
if ($target.first().attr('aria-expanded') === 'true') {
......@@ -578,12 +578,12 @@ class Backend extends Workspaces {
'expand',
'apps-pagetree-collapse',
).attr('title', TYPO3.lang['tooltip.expand'])
.attr('data-target', '[data-collection="' + item.Workspaces_CollectionCurrent + '"]')
.attr('data-toggle', 'collapse'),
.attr('data-bs-target', '[data-collection="' + item.Workspaces_CollectionCurrent + '"]')
.attr('data-bs-toggle', 'collapse'),
$('<button />', {
class: 'btn btn-default',
'data-action': 'changes',
'data-toggle': 'tooltip',
'data-bs-toggle': 'tooltip',
title: TYPO3.lang['tooltip.showChanges'],
}).append(this.getPreRenderedIcon('actions-document-info')),
this.getAction(
......@@ -616,9 +616,9 @@ class Backend extends Workspaces {
if (item.integrity.messages !== '') {
$integrityIcon = $(TYPO3.settings.Workspaces.icons[item.integrity.status]);
$integrityIcon
.attr('data-toggle', 'tooltip')
.attr('data-placement', 'top')
.attr('data-html', 'true')
.attr('data-bs-toggle', 'tooltip')
.attr('data-bs-placement', 'top')
.attr('data-bs-html', 'true')
.attr('title', item.integrity.messages);
}
......@@ -680,7 +680,7 @@ class Backend extends Workspaces {
),
);
Tooltip.initialize('[data-toggle="tooltip"]', {
Tooltip.initialize('[data-bs-toggle="tooltip"]', {
delay: {
show: 500,
hide: 100,
......@@ -786,7 +786,7 @@ class Backend extends Workspaces {
href: '#workspace-changes',
'aria-controls': 'workspace-changes',
role: 'tab',
'data-toggle': 'tab',
'data-bs-toggle': 'tab',
}).text(TYPO3.lang['window.recordChanges.tabs.changeSummary']),
),
);
......@@ -807,7 +807,7 @@ class Backend extends Workspaces {
href: '#workspace-comments',
'aria-controls': 'workspace-comments',
role: 'tab',
'data-toggle': 'tab',
'data-bs-toggle': 'tab',
}).html(TYPO3.lang['window.recordChanges.tabs.comments'] + '&nbsp;').append(
$('<span />', {class: 'badge'}).text(item.comments.length),
),
......@@ -830,7 +830,7 @@ class Backend extends Workspaces {
href: '#workspace-history',
'aria-controls': 'workspace-history',
role: 'tab',
'data-toggle': 'tab',
'data-bs-toggle': 'tab',
}).text(TYPO3.lang['window.recordChanges.tabs.history']),
),
);
......@@ -1203,7 +1203,7 @@ class Backend extends Workspaces {
return $('<button />', {
class: 'btn btn-default',
'data-action': action,
'data-toggle': 'tooltip',
'data-bs-toggle': 'tooltip',
}).append(this.getPreRenderedIcon(iconIdentifier));
}
return $('<span />', {class: 'btn btn-default disabled'}).append(this.getPreRenderedIcon('empty-empty'));
......
......@@ -71,6 +71,7 @@
},
"dependencies": {
"@claviska/jquery-minicolors": "^2.3.5",
"@popperjs/core": "^2.6.0",
"@typo3/icons": "^2.0.4",
"autosize": "^4.0.2",
"bootstrap": "^5.0.0-beta1",
......
......@@ -245,6 +245,11 @@
"@nodelib/fs.scandir" "2.1.3"
fastq "^1.6.0"
"@popperjs/core@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.6.0.tgz#f022195afdfc942e088ee2101285a1d31c7d727f"
integrity sha512-cPqjjzuFWNK3BSKLm0abspP0sp/IGOli4p5I5fKFAzdS8fvjdOwDCfZqAaIiXd9lPkOWi3SUUfZof3hEb7J/uw==
"@sindresorhus/is@^0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
......
......@@ -6,8 +6,8 @@
<f:translate key="{label}" default="{label}" extensionName="adminpanel"/>
</label>
<div class="typo3-adminPanel-form-datetime-input-group">
<input type="date" id="{name}-date-hr" class="typo3-adminPanel-form-datetime-input" data-target="{name}"/>
<input type="time" id="{name}-time-hr" class="typo3-adminPanel-form-datetime-input" data-target="{name}"/>
<input type="date" id="{name}-date-hr" class="typo3-adminPanel-form-datetime-input" data-bs-target="{name}"/>
<input type="time" id="{name}-time-hr" class="typo3-adminPanel-form-datetime-input" data-bs-target="{name}"/>
</div>
<input type="hidden" name="TSFE_ADMIN_PANEL[{name}]" id="{name}" value="{value}" />
</div>
......
......@@ -323,7 +323,7 @@ class BackendController
$toolbar[] = '<li ' . GeneralUtility::implodeAttributes($liAttributes, true) . '>';
if ($hasDropDown) {
$toolbar[] = '<a href="#" class="toolbar-item-link dropdown-toggle" data-toggle="dropdown">';
$toolbar[] = '<a href="#" class="toolbar-item-link dropdown-toggle" data-bs-toggle="dropdown">';
$toolbar[] = $toolbarItem->getItem();
$toolbar[] = '</a>';
$toolbar[] = '<div class="dropdown-menu" role="menu">';
......
......@@ -221,7 +221,7 @@ class InlineRecordContainer extends AbstractContainer
$ariaAttributesString = 'aria-expanded="' . $ariaExpanded . '" aria-controls="' . $ariaControls . '"';
$html = '
<div ' . GeneralUtility::implodeAttributes($containerAttributes, true) . '>
<div class="panel-heading" data-toggle="formengine-inline" id="' . htmlspecialchars($hashedObjectId, ENT_QUOTES | ENT_HTML5) . '_header" data-expandSingle="' . ($inlineConfig['appearance']['expandSingle'] ? 1 : 0) . '">
<div class="panel-heading" data-bs-toggle="formengine-inline" id="' . htmlspecialchars($hashedObjectId, ENT_QUOTES | ENT_HTML5) . '_header" data-expandSingle="' . ($inlineConfig['appearance']['expandSingle'] ? 1 : 0) . '">
<div class="form-irre-header">
<div class="form-irre-header-cell form-irre-header-icon">
<span class="caret"></span>
......@@ -614,7 +614,7 @@ class InlineRecordContainer extends AbstractContainer
// If the record is edit-locked by another user, we will show a little warning sign:
if ($lockInfo = BackendUtility::isRecordLocked($foreignTable, $rec['uid'])) {
$cells['locked'] = '
<button type="button" class="btn btn-default" data-toggle="tooltip" data-title="' . htmlspecialchars($lockInfo['msg']) . '">
<button type="button" class="btn btn-default" data-bs-toggle="tooltip" data-title="' . htmlspecialchars($lockInfo['msg']) . '">
<span title="' . htmlspecialchars($lockInfo['msg']) . '">' . $this->iconFactory->getIcon('warning-in-use', Icon::SIZE_SMALL)->render() . '</span>
</button>';
}
......
......@@ -223,7 +223,7 @@ class InputLinkElement extends AbstractFormElement
$expansionHtml[] = '<div class="form-wizards-element">';
$expansionHtml[] = '<div class="input-group t3js-form-field-inputlink">';
$expansionHtml[] = '<span class="t3js-form-field-inputlink-icon input-group-addon">' . $linkExplanation['icon'] . '</span>';
$expansionHtml[] = '<input class="form-control t3js-form-field-inputlink-explanation" data-toggle="tooltip" data-title="' . $explanation . '" value="' . $explanation . '" readonly>';
$expansionHtml[] = '<input class="form-control t3js-form-field-inputlink-explanation" data-bs-toggle="tooltip" data-title="' . $explanation . '" value="' . $explanation . '" readonly>';
$expansionHtml[] = '<input type="text" ' . GeneralUtility::implodeAttributes($attributes, true) . ' />';
$expansionHtml[] = '<span class="input-group-btn">';
$expansionHtml[] = '<button class="btn btn-default t3js-form-field-inputlink-explanation-toggle" type="button" title="' . htmlspecialchars($toggleButtonTitle) . '">';
......
......@@ -122,7 +122,7 @@ class InputSlugElement extends AbstractFormElement
// field to manipulate the value, and the final hidden field used to send the value
$mainFieldHtml[] = '<input';
$mainFieldHtml[] = ' class="form-control t3js-form-field-slug-readonly"';
$mainFieldHtml[] = ' data-toggle="tooltip"';
$mainFieldHtml[] = ' data-bs-toggle="tooltip"';
$mainFieldHtml[] = ' data-title="' . htmlspecialchars($itemValue) . '"';
$mainFieldHtml[] = ' value="' . htmlspecialchars($itemValue) . '"';
$mainFieldHtml[] = ' readonly';
......
......@@ -177,7 +177,7 @@ class SelectCheckBoxElement extends AbstractFormElement
$html[] = '<div id="' . $groupId . '" class="panel panel-default">';
if (is_array($group['header'])) {
$html[] = '<div class="panel-heading">';
$html[] = '<a data-toggle="collapse" href="#' . $groupIdCollapsible . '" aria-expanded="false" aria-controls="' . $groupIdCollapsible . '">';
$html[] = '<a data-bs-toggle="collapse" href="#' . $groupIdCollapsible . '" aria-expanded="false" aria-controls="' . $groupIdCollapsible . '">';
$html[] = $group['header']['icon'];
$html[] = htmlspecialchars($group['header']['title']);
$html[] = '</a>';
......@@ -231,7 +231,7 @@ class SelectCheckBoxElement extends AbstractFormElement
$html[] = '<thead>';
$html[] = '<tr>';
$html[] = '<th class="col-checkbox">';
$html[] = '<input type="checkbox" id="' . $checkboxId . '" class="t3js-toggle-checkboxes" data-trigger="hover" data-placement="right" data-title="' . $title . '" data-toggle="tooltip" />';
$html[] = '<input type="checkbox" id="' . $checkboxId . '" class="t3js-toggle-checkboxes" data-bs-trigger="hover" data-bs-placement="right" data-title="' . $title . '" data-bs-toggle="tooltip" />';
$html[] = '</th>';
$html[] = '<th class="col-title" colspan="2"><label for="' . $checkboxId . '">' . $title . '</label></th>';
$html[] = '<th class="text-right">' . $resetGroupBtn . '</th>';
......
......@@ -47,7 +47,7 @@ class InlineStackProcessor
/**
* Convert the DOM object-id of an inline container to an array.
* The object-id could look like 'data-parentPageId-tx_mmftest_company-1-employees'.
* The object-id could look like 'data-bs-parentPageId-tx_mmftest_company-1-employees'.
* This initializes $this->inlineStructure - used by AJAX entry points
* There are two keys:
* - 'stable': Containing full qualified identifiers (table, uid and field)
......
......@@ -159,8 +159,7 @@ class SplitButton extends AbstractButton
' . $items['primary']->getIcon()->render('inline') . '
' . htmlspecialchars($items['primary']->getTitle()) . '
</button>
<button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<span class="caret"></span>
<button type="button" class="btn btn-sm btn-default dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">';
......@@ -182,6 +181,7 @@ class SplitButton extends AbstractButton
if (!empty($option->getOnClick())) {
$optionAttributes['onclick'] = $option->getOnClick();
}
$optionAttributes['class'] = $optionAttributes['class'] ? $optionAttributes['class'] . ' dropdown-item' : 'dropdown-item';
$optionAttributesString = '';
foreach ($optionAttributes as $key => $value) {
$optionAttributesString .= ' ' . htmlspecialchars($key) . '="' . htmlspecialchars($value) . '"';
......
......@@ -530,7 +530,7 @@ class PagePositionMap
$uid = '';
}
$cc = hexdec(substr(md5($uid . '-' . $vv . '-' . $kk), 0, 4));
return '<a href="#" onclick="' . htmlspecialchars($this->onClickInsertRecord($row, $vv, $moveUid, $pid, $this->cur_sys_language)) . '" data-dismiss="modal"><i class="t3-icon fa fa-long-arrow-left" name="mImgEnd' . $cc . '" title="' . htmlspecialchars($this->getLanguageService()->getLL($this->l_insertNewRecordHere)) . '"></i></a>';
return '<a href="#" onclick="' . htmlspecialchars($this->onClickInsertRecord($row, $vv, $moveUid, $pid, $this->cur_sys_language)) . '" data-bs-dismiss="modal"><i class="t3-icon fa fa-long-arrow-left" name="mImgEnd' . $cc . '" title="' . htmlspecialchars($this->getLanguageService()->getLL($this->l_insertNewRecordHere)) . '"></i></a>';
}
/**
......
......@@ -1200,9 +1200,9 @@ class BackendUtility
public static function getRecordToolTip(array $row, $table = 'pages')
{
$toolTipText = self::getRecordIconAltText($row, $table);
$toolTipCode = 'data-toggle="tooltip" data-title=" '
$toolTipCode = 'data-bs-toggle="tooltip" data-title=" '
. str_replace(' - ', '<br>', $toolTipText)
. '" data-html="true" data-placement="right"';
. '" data-bs-html="true" data-bs-placement="right"';
return $toolTipCode;
}
......
......@@ -173,7 +173,7 @@ class GridColumnItem extends AbstractGridObject
}
if ($lockInfo = BackendUtility::isRecordLocked('tt_content', $row['uid'])) {
$icons[] = '<a href="#" data-toggle="tooltip" data-title="' . htmlspecialchars($lockInfo['msg']) . '">'
$icons[] = '<a href="#" data-bs-toggle="tooltip" data-title="' . htmlspecialchars($lockInfo['msg']) . '">'
. $this->iconFactory->getIcon('warning-in-use', Icon::SIZE_SMALL)->render() . '</a>';
}
......
......@@ -332,7 +332,7 @@ class BackendLayoutRenderer
{
$pasteIcon = json_encode(
' <button type="button"'
. ' data-content="' . htmlspecialchars((string)$pasteItem) . '"'
. ' data-bs-content="' . htmlspecialchars((string)$pasteItem) . '"'
. ' data-title="' . htmlspecialchars($pasteTitle) . '"'
. ' data-severity="warning"'
. ' class="t3js-paste t3js-paste' . htmlspecialchars($copyMode) . ' ' . htmlspecialchars($cssClass) . ' btn btn-default btn-sm"'
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment