From 787ad770829171bf93d20a7ce53d3e14e6bdca3c Mon Sep 17 00:00:00 2001 From: Artem Goncharov Date: Thu, 10 Oct 2024 11:45:26 +0200 Subject: [PATCH] Move to bootstrap5 in openstackdocstheme we upgrade to Bootstrapv5 (5 years later than should have done). There are major changes that need to be addressed to make os-api-ref look nearly like it was before - bootstrap dropped jQuery and thus extension js need to be initialized differently - icons dropped from bootstrap - combobox (dropdown) is part of the bundled bootstrap and no known project uses it anyway (so no way to try) - expand links and text replacement doesn't work anyway without anybody noticing (or at least not with browsers I use). Depends-by: https://review.opendev.org/c/openstack/openstackdocstheme/+/931837 Change-Id: I907562ea2646a0316dc99befcdf93c0f0bf7765c --- os_api_ref/__init__.py | 21 +- os_api_ref/assets/api-site.css | 10 +- os_api_ref/assets/api-site.js | 18 +- os_api_ref/assets/combobox.js | 462 ------------------------- os_api_ref/tests/test_basic_example.py | 8 +- os_api_ref/tests/test_microversions.py | 6 +- 6 files changed, 30 insertions(+), 495 deletions(-) delete mode 100644 os_api_ref/assets/combobox.js diff --git a/os_api_ref/__init__.py b/os_api_ref/__init__.py index 6416164..4568998 100644 --- a/os_api_ref/__init__.py +++ b/os_api_ref/__init__.py @@ -487,30 +487,32 @@ class RestParametersDirective(Table): def rest_method_html(self, node): tmpl = """ -
+
- - %(method)s + + %(method)s
-
%(url)s
-

%(desc)s

+
%(url)s
+
+

%(desc)s

+
@@ -532,7 +534,7 @@ def rest_expand_all_html(self, node): tmpl = """
%(extra_js)s -
+
%(selector)s
@@ -634,7 +636,7 @@ def resolve_rest_references(app, doctree): def copy_assets(app, exception): - assets = ('api-site.css', 'api-site.js', 'combobox.js') + assets = ('api-site.css', 'api-site.js') fonts = ( 'glyphicons-halflings-regular.ttf', 'glyphicons-halflings-regular.woff' @@ -660,7 +662,6 @@ def copy_assets(app, exception): def add_assets(app): app.add_css_file('api-site.css') app.add_js_file('api-site.js') - app.add_js_file('combobox.js') def setup(app): diff --git a/os_api_ref/assets/api-site.css b/os_api_ref/assets/api-site.css index 519c778..7c12897 100644 --- a/os_api_ref/assets/api-site.css +++ b/os_api_ref/assets/api-site.css @@ -12,7 +12,7 @@ larger, we need them smaller */ blockquote { font-size: 1em; } .docs-book-wrapper { - width: 90% !important + max-width: 90% !important } tbody>tr:nth-child(odd)>td, @@ -58,6 +58,10 @@ a.operation-anchor { font-weight: bold; } +span.badge { + /* backwards compatibility to BS3 */ + border-radius: 0.25em; +} .label-POST { background-color: #5cb85c; } @@ -100,6 +104,10 @@ a.operation-anchor { background-color: #c9302c; } +button.btn-info { + color: white; +} + .btn-detail:hover, .btn-detail:focus, .btn-expand-all:hover, .btn-expand-all:focus { color: #fff; diff --git a/os_api_ref/assets/api-site.js b/os_api_ref/assets/api-site.js index 5d0b83e..e5c0bc6 100644 --- a/os_api_ref/assets/api-site.js +++ b/os_api_ref/assets/api-site.js @@ -1,4 +1,4 @@ -(function() { +window.onload = function() { // the list of expanded element ids var expanded = []; // whether we should sync expand changes with the location @@ -161,18 +161,4 @@ $('[class^=rp_max_ver]').show(400); } - - $(document).ready(function(){ - $('#mv_select').combobox({appendId: '-visable'}); - $('#mv_select').on('change', function() { - var version = this.value; - if (version == "") { - reset_microversion(); - } else { - set_microversion(version); - } - }); - }); - - -})(); +}; diff --git a/os_api_ref/assets/combobox.js b/os_api_ref/assets/combobox.js deleted file mode 100644 index d69a7fe..0000000 --- a/os_api_ref/assets/combobox.js +++ /dev/null @@ -1,462 +0,0 @@ -/* ============================================================= - * bootstrap-combobox.js v1.1.7 - * ============================================================= - * Copyright 2012 Daniel Farrell - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============================================================ */ - -!function( $ ) { - - "use strict"; - - /* COMBOBOX PUBLIC CLASS DEFINITION - * ================================ */ - - var Combobox = function ( element, options ) { - this.options = $.extend({}, $.fn.combobox.defaults, options); - this.template = this.options.template || this.template - this.$source = $(element); - this.$container = this.setup(); - this.$element = this.$container.find('input[type=text]'); - this.$target = this.$container.find('input[type=hidden]'); - this.$button = this.$container.find('.dropdown-toggle'); - this.$menu = $(this.options.menu).appendTo('body'); - this.matcher = this.options.matcher || this.matcher; - this.sorter = this.options.sorter || this.sorter; - this.highlighter = this.options.highlighter || this.highlighter; - this.shown = false; - this.selected = false; - this.refresh(); - this.transferAttributes(); - this.listen(); - }; - - Combobox.prototype = { - - constructor: Combobox - - , setup: function () { - var combobox = $(this.template()); - this.$source.before(combobox); - this.$source.hide(); - return combobox; - } - - , disable: function() { - this.$element.prop('disabled', true); - this.$button.attr('disabled', true); - this.disabled = true; - this.$container.addClass('combobox-disabled'); - } - - , enable: function() { - this.$element.prop('disabled', false); - this.$button.attr('disabled', false); - this.disabled = false; - this.$container.removeClass('combobox-disabled'); - } - , parse: function () { - var that = this - , map = {} - , source = [] - , selected = false - , selectedValue = ''; - this.$source.find('option').each(function() { - var option = $(this); - if (option.val() === '') { - that.options.placeholder = option.text(); - return; - } - map[option.text()] = option.val(); - source.push(option.text()); - if (option.prop('selected')) { - selected = option.text(); - selectedValue = option.val(); - } - }) - this.map = map; - if (selected) { - this.$element.val(selected); - this.$target.val(selectedValue); - this.$container.addClass('combobox-selected'); - this.selected = true; - } - return source; - } - - , transferAttributes: function() { - this.options.placeholder = this.$source.attr('data-placeholder') || this.options.placeholder - if(this.options.appendId !== "undefined") { - this.$element.attr('id', this.$source.attr('id') + this.options.appendId); - } - this.$element.attr('placeholder', this.options.placeholder) - this.$target.prop('name', this.$source.prop('name')) - this.$target.val(this.$source.val()) - this.$source.removeAttr('name') // Remove from source otherwise form will pass parameter twice. - this.$element.attr('required', this.$source.attr('required')) - this.$element.attr('rel', this.$source.attr('rel')) - this.$element.attr('title', this.$source.attr('title')) - this.$element.attr('class', this.$source.attr('class')) - this.$element.attr('tabindex', this.$source.attr('tabindex')) - this.$source.removeAttr('tabindex') - if (this.$source.attr('disabled')!==undefined) - this.disable(); - } - - , select: function () { - var val = this.$menu.find('.active').attr('data-value'); - this.$element.val(this.updater(val)).trigger('change'); - this.$target.val(this.map[val]).trigger('change'); - this.$source.val(this.map[val]).trigger('change'); - this.$container.addClass('combobox-selected'); - this.selected = true; - return this.hide(); - } - - , updater: function (item) { - return item; - } - - , show: function () { - var pos = $.extend({}, this.$element.position(), { - height: this.$element[0].offsetHeight - }); - - this.$menu - .insertAfter(this.$element) - .css({ - top: pos.top + pos.height - , left: pos.left - }) - .show(); - - $('.dropdown-menu').on('mousedown', $.proxy(this.scrollSafety, this)); - - this.shown = true; - return this; - } - - , hide: function () { - this.$menu.hide(); - $('.dropdown-menu').off('mousedown', $.proxy(this.scrollSafety, this)); - this.$element.on('blur', $.proxy(this.blur, this)); - this.shown = false; - return this; - } - - , lookup: function (event) { - this.query = this.$element.val(); - return this.process(this.source); - } - - , process: function (items) { - var that = this; - - items = $.grep(items, function (item) { - return that.matcher(item); - }) - - items = this.sorter(items); - - if (!items.length) { - return this.shown ? this.hide() : this; - } - - return this.render(items.slice(0, this.options.items)).show(); - } - - , template: function() { - if (this.options.bsVersion == '2') { - return '
' - } else { - return '
' - } - } - - , matcher: function (item) { - return ~item.toLowerCase().indexOf(this.query.toLowerCase()); - } - - , sorter: function (items) { - var beginswith = [] - , caseSensitive = [] - , caseInsensitive = [] - , item; - - while (item = items.shift()) { - if (!item.toLowerCase().indexOf(this.query.toLowerCase())) {beginswith.push(item);} - else if (~item.indexOf(this.query)) {caseSensitive.push(item);} - else {caseInsensitive.push(item);} - } - - return beginswith.concat(caseSensitive, caseInsensitive); - } - - , highlighter: function (item) { - var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&'); - return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) { - return '' + match + ''; - }) - } - - , render: function (items) { - var that = this; - - items = $(items).map(function (i, item) { - i = $(that.options.item).attr('data-value', item); - i.find('a').html(that.highlighter(item)); - return i[0]; - }) - - items.first().addClass('active'); - this.$menu.html(items); - return this; - } - - , next: function (event) { - var active = this.$menu.find('.active').removeClass('active') - , next = active.next(); - - if (!next.length) { - next = $(this.$menu.find('li')[0]); - } - - next.addClass('active'); - } - - , prev: function (event) { - var active = this.$menu.find('.active').removeClass('active') - , prev = active.prev(); - - if (!prev.length) { - prev = this.$menu.find('li').last(); - } - - prev.addClass('active'); - } - - , toggle: function () { - if (!this.disabled) { - if (this.$container.hasClass('combobox-selected')) { - this.clearTarget(); - this.triggerChange(); - this.clearElement(); - } else { - if (this.shown) { - this.hide(); - } else { - this.clearElement(); - this.lookup(); - } - } - } - } - - , scrollSafety: function(e) { - if (e.target.tagName == 'UL') { - this.$element.off('blur'); - } - } - , clearElement: function () { - this.$element.val('').focus(); - } - - , clearTarget: function () { - this.$source.val(''); - this.$target.val(''); - this.$container.removeClass('combobox-selected'); - this.selected = false; - } - - , triggerChange: function () { - this.$source.trigger('change'); - } - - , refresh: function () { - this.source = this.parse(); - this.options.items = this.source.length; - } - - , listen: function () { - this.$element - .on('focus', $.proxy(this.focus, this)) - .on('blur', $.proxy(this.blur, this)) - .on('keypress', $.proxy(this.keypress, this)) - .on('keyup', $.proxy(this.keyup, this)); - - if (this.eventSupported('keydown')) { - this.$element.on('keydown', $.proxy(this.keydown, this)); - } - - this.$menu - .on('click', $.proxy(this.click, this)) - .on('mouseenter', 'li', $.proxy(this.mouseenter, this)) - .on('mouseleave', 'li', $.proxy(this.mouseleave, this)); - - this.$button - .on('click', $.proxy(this.toggle, this)); - } - - , eventSupported: function(eventName) { - var isSupported = eventName in this.$element; - if (!isSupported) { - this.$element.setAttribute(eventName, 'return;'); - isSupported = typeof this.$element[eventName] === 'function'; - } - return isSupported; - } - - , move: function (e) { - if (!this.shown) {return;} - - switch(e.keyCode) { - case 9: // tab - case 13: // enter - case 27: // escape - e.preventDefault(); - break; - - case 38: // up arrow - e.preventDefault(); - this.prev(); - this.fixMenuScroll(); - break; - - case 40: // down arrow - e.preventDefault(); - this.next(); - this.fixMenuScroll(); - break; - } - - e.stopPropagation(); - } - - , fixMenuScroll: function(){ - var active = this.$menu.find('.active'); - if(active.length){ - var top = active.position().top; - var bottom = top + active.height(); - var scrollTop = this.$menu.scrollTop(); - var menuHeight = this.$menu.height(); - if(bottom > menuHeight){ - this.$menu.scrollTop(scrollTop + bottom - menuHeight); - } else if(top < 0){ - this.$menu.scrollTop(scrollTop + top); - } - } - } - - , keydown: function (e) { - this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27]); - this.move(e); - } - - , keypress: function (e) { - if (this.suppressKeyPressRepeat) {return;} - this.move(e); - } - - , keyup: function (e) { - switch(e.keyCode) { - case 40: // down arrow - if (!this.shown){ - this.toggle(); - } - break; - case 39: // right arrow - case 38: // up arrow - case 37: // left arrow - case 36: // home - case 35: // end - case 16: // shift - case 17: // ctrl - case 18: // alt - break; - - case 9: // tab - case 13: // enter - if (!this.shown) {return;} - this.select(); - break; - - case 27: // escape - if (!this.shown) {return;} - this.hide(); - break; - - default: - this.clearTarget(); - this.lookup(); - } - - e.stopPropagation(); - e.preventDefault(); - } - - , focus: function (e) { - this.focused = true; - } - - , blur: function (e) { - var that = this; - this.focused = false; - var val = this.$element.val(); - if (!this.selected && val !== '' ) { - this.$element.val(''); - this.$source.val('').trigger('change'); - this.$target.val('').trigger('change'); - } - if (!this.mousedover && this.shown) {setTimeout(function () { that.hide(); }, 200);} - } - - , click: function (e) { - e.stopPropagation(); - e.preventDefault(); - this.select(); - this.$element.focus(); - } - - , mouseenter: function (e) { - this.mousedover = true; - this.$menu.find('.active').removeClass('active'); - $(e.currentTarget).addClass('active'); - } - - , mouseleave: function (e) { - this.mousedover = false; - } - }; - - /* COMBOBOX PLUGIN DEFINITION - * =========================== */ - $.fn.combobox = function ( option ) { - return this.each(function () { - var $this = $(this) - , data = $this.data('combobox') - , options = typeof option == 'object' && option; - if(!data) {$this.data('combobox', (data = new Combobox(this, options)));} - if (typeof option == 'string') {data[option]();} - }); - }; - - $.fn.combobox.defaults = { - bsVersion: '3' - , menu: '' - , item: '
  • ' - }; - - $.fn.combobox.Constructor = Combobox; - -}( window.jQuery ); diff --git a/os_api_ref/tests/test_basic_example.py b/os_api_ref/tests/test_basic_example.py index ba78658..56e71bb 100644 --- a/os_api_ref/tests/test_basic_example.py +++ b/os_api_ref/tests/test_basic_example.py @@ -57,17 +57,17 @@ class TestBasicExample(base.TestCase): # whole template instead of parts. content = str(self.soup.find_all(class_='operation-grp')) self.assertIn( - '', + '', str(content)) self.assertIn( - 'GET', + 'GET', str(content)) self.assertIn( - '
    /servers
    ', + '
    /servers
    ', str(content)) self.assertIn( (''), str(content)) diff --git a/os_api_ref/tests/test_microversions.py b/os_api_ref/tests/test_microversions.py index d6794a4..a6c36fa 100644 --- a/os_api_ref/tests/test_microversions.py +++ b/os_api_ref/tests/test_microversions.py @@ -46,11 +46,13 @@ class TestMicroversions(base.TestCase): content = self.soup.find_all(class_='rp_min_ver_2_17') self.assertRegex( str(content[0]), - '^