diff --git a/.versionrc b/.versionrc new file mode 100644 index 00000000000..da7d73a2476 --- /dev/null +++ b/.versionrc @@ -0,0 +1,43 @@ +{ + "scripts": { + "postchangelog": "./node_modules/.bin/prettier --write CHANGELOG.md" + }, + "skip": { + "commit": true, + "tag": true + }, + "types": [ + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "feat", + "section": "Features" + }, + { + "type": "perf", + "section": "Performance" + }, + { + "type": "docs", + "hidden": true + }, + { + "type": "style", + "hidden": true + }, + { + "type": "refactor", + "hidden": true + }, + { + "type": "chore", + "hidden": true + }, + { + "type": "test", + "hidden": true + } + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index dea60b74bcf..497255d7a3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,25 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. + + +### [v2.17.2](https://github.com/bootstrap-vue/bootstrap-vue/compare/v2.17.1...v2.17.2) + +Released: 2020-09-18 + +### Bug Fixes v2.17.2 + +- **b-nav-item-dropdown:** `boundary` handling in `` (closes + [#5789](https://github.com/bootstrap-vue/bootstrap-vue/issues/5789)) + ([#5794](https://github.com/bootstrap-vue/bootstrap-vue/issues/5794)) + ([73383bf](https://github.com/bootstrap-vue/bootstrap-vue/commit/73383bfd935c097604bf5ad39a9cc2d18961ba87)) +- **b-skeleton:** add missing component exports + ([#5806](https://github.com/bootstrap-vue/bootstrap-vue/issues/5806)) + ([871ce22](https://github.com/bootstrap-vue/bootstrap-vue/commit/871ce22504c4e64348b844c0e4306161317abf60)) +- **b-tooltip, b-popover:** fix `title` not being reset on hide + ([#5793](https://github.com/bootstrap-vue/bootstrap-vue/issues/5793)) + ([31eeb0a](https://github.com/bootstrap-vue/bootstrap-vue/commit/31eeb0ab5ef262c33579f43969c7d6ee6c802e3d)) + ### [v2.17.1](https://github.com/bootstrap-vue/bootstrap-vue/compare/v2.17.0...v2.17.1) diff --git a/docs/common-props.json b/docs/common-props.json index 6267ab2c657..2be2f4b6370 100644 --- a/docs/common-props.json +++ b/docs/common-props.json @@ -126,7 +126,7 @@ "description": "Sets the `placeholder` attribute value on the form control" }, "disabled": { - "description": "When set to 'true', disables the component's functionality and places it in a disabled state" + "description": "When set to `true`, disables the component's functionality and places it in a disabled state" }, "readonly": { "description": "Sets the `readonly` attribute on the form control" diff --git a/package.json b/package.json index df3b35147a7..217e9576daf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bootstrap-vue", - "version": "2.17.1", + "version": "2.17.2", "description": "With more than 85 components, over 45 available plugins, several directives, and 1000+ icons, BootstrapVue provides one of the most comprehensive implementations of the Bootstrap v4 component and grid system available for Vue.js v2.6, complete with extensive and automated WAI-ARIA accessibility markup.", "main": "dist/bootstrap-vue.common.js", "web": "dist/bootstrap-vue.js", @@ -149,125 +149,83 @@ "marked": "^1.1.1", "node-sass": "^4.14.1", "nuxt": "^2.14.5", - "postcss": "^7.0.32", + "postcss": "^7.0.34", "postcss-cli": "^7.1.2", "prettier": "1.14.3", "require-context": "^1.1.0", - "rollup": "^2.27.0", + "rollup": "^2.27.1", "rollup-plugin-babel": "^4.4.0", "rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-node-resolve": "^5.2.0", "sass-loader": "^10.0.2", "standard-version": "^9.0.0", - "terser": "^5.3.1", + "terser": "^5.3.2", "vue": "^2.6.12", "vue-jest": "^3.0.7", "vue-router": "^3.4.3", "vue-server-renderer": "^2.6.12", "vue-template-compiler": "^2.6.12" }, - "standard-version": { - "scripts": { - "postchangelog": "./node_modules/.bin/prettier --write CHANGELOG.md" - }, - "types": [ - { - "type": "fix", - "section": "Bug Fixes" - }, - { - "type": "feat", - "section": "Features" - }, - { - "type": "perf", - "section": "Performance" - }, - { - "type": "docs", - "hidden": true - }, - { - "type": "style", - "hidden": true - }, - { - "type": "refactor", - "hidden": true - }, - { - "type": "chore", - "hidden": true - }, - { - "type": "test", - "hidden": true - } - ] - }, "keywords": [ "Bootstrap", "Bootstrap v4", "Bootstrap for Vue", + "Vue", + "Vue.js", + "Vue v2", "SSR", "Web", "Components", "Directives", + "Icons", + "Bootstrap Icons", "ARIA", "Accessibility", "a11y", - "Polymer", - "Vue", - "VueJS", - "Vue2", - "WebComponents", - "jquery", "Popper.js", - "Popper", "CSS", "SCSS", - "Icons", - "Bootstrap Icons", - "Flexbox", "Alert", + "Avatar", + "Badge", "Breadcrumb", "Button", - "Checkbox", - "Radio", + "Calendar", "Card", "Carousel", - "Slider", - "Calendar", + "Checkbox", + "Collapse", + "Collapse", "Date picker", "Datepicker", - "Collapse", "Dropdown", + "Dropzone", "Form", - "Select", - "Option", "Input", "Jumbotron", "List", - "Nav", "Modal", - "MessageBox", - "Upload", - "Dropzone", + "Nav", "Navbar", + "Option", + "Overlay", "Pagination", "Popover", "Progress", + "Radio", + "Select", + "Sidebar", + "Skeleton", + "Slider", "Spinner", "Tab", "Table", "Tag", "Tags", + "Time picker", + "Timepicker", "Toast", - "Tooltip", - "vue-bootstrap", - "vue-strap", - "vuestrap", - "uiv" + "Tooltip" ], "collective": { "type": "opencollective", diff --git a/src/components/form-rating/README.md b/src/components/form-rating/README.md index e381da62de0..8fb7c0c60e9 100644 --- a/src/components/form-rating/README.md +++ b/src/components/form-rating/README.md @@ -295,7 +295,7 @@ may prefer the custom input to occupy on the space required for it's contents. S ### Borderless -By default, `` has standard Bootstrap form-control styling. To disable the default +By default, `` has standard Bootstrap form-control styling. To disable the default form-control border, simply set the `no-border` prop to `true`. ```html diff --git a/src/components/form-tags/README.md b/src/components/form-tags/README.md index e8434eb82d1..87d934a0d7b 100644 --- a/src/components/form-tags/README.md +++ b/src/components/form-tags/README.md @@ -833,7 +833,7 @@ You can easily create a custom wrapper component with your preferred rendering s import { BFormTags } from 'bootstrap-vue' export default { - name: 'MyCustomTags", + name: 'MyCustomTags', components: { BFormTags }, model: { prop: 'value', diff --git a/src/components/nav/nav-item-dropdown.js b/src/components/nav/nav-item-dropdown.js index 176203e0cdb..fc783dc5253 100644 --- a/src/components/nav/nav-item-dropdown.js +++ b/src/components/nav/nav-item-dropdown.js @@ -29,10 +29,6 @@ export const BNavItemDropdown = /*#__PURE__*/ Vue.extend({ toggleId() { return this.safeId('_BV_toggle_') }, - isNav() { - // Signal to dropdown mixin that we are in a navbar - return true - }, dropdownClasses() { return [this.directionClass, this.boundaryClass, { show: this.visible }] }, diff --git a/src/components/nav/nav-item-dropdown.spec.js b/src/components/nav/nav-item-dropdown.spec.js index df15c5e5b5a..f9a115cd1ad 100644 --- a/src/components/nav/nav-item-dropdown.spec.js +++ b/src/components/nav/nav-item-dropdown.spec.js @@ -34,15 +34,6 @@ describe('nav-item-dropdown', () => { wrapper.destroy() }) - it('should have a flag that we are in a nav', async () => { - const wrapper = mount(BNavItemDropdown) - - expect(wrapper.vm).toBeDefined() - expect(wrapper.vm.isNav).toBe(true) - - wrapper.destroy() - }) - it('should have custom toggle class when "toggle-class" prop set', async () => { const wrapper = mount(BNavItemDropdown, { propsData: { diff --git a/src/components/nav/package.json b/src/components/nav/package.json index 192dcfbed5c..805bb3f8739 100644 --- a/src/components/nav/package.json +++ b/src/components/nav/package.json @@ -98,11 +98,11 @@ "props": [ { "prop": "text", - "description": "Text to place in the toggle button, or in the split button is split mode" + "description": "Text to place in the toggle element (link)" }, { "prop": "html", - "description": "HTML string to place in the toggle button, or in the split button is split mode. Use with caution" + "description": "HTML string to place in the toggle element (link). Use with caution" }, { "prop": "dropup", @@ -138,16 +138,16 @@ }, { "prop": "toggleClass", - "description": "CSS class (or classes) to add to the toggle button" + "description": "CSS class (or classes) to add to the toggle element (link)" }, { "prop": "noCaret", - "description": "Hide the caret indicator on the toggle button" + "description": "Hide the caret indicator on the toggle element (link)" }, { "prop": "boundary", "version": "2.4.0", - "description": "The boundary constraint of the menu: 'scrollParent', 'window', 'viewport', or a reference to an HTMLElement. Has no effect when dropdown is inside a b-navbar" + "description": "The boundary constraint of the menu: 'scrollParent', 'window', 'viewport', or a reference to an HTMLElement. Has no effect when dropdown is inside a ``" } ], "slots": [ diff --git a/src/components/popover/package.json b/src/components/popover/package.json index be889f05bd0..50e9dc9f4df 100644 --- a/src/components/popover/package.json +++ b/src/components/popover/package.json @@ -29,7 +29,7 @@ }, { "prop": "placement", - "description": "Placement of the popover: One of 'top', 'bottom', 'right', 'left', 'top-left', 'top-right', 'bottom-left', 'bottom-right', 'left-top', 'left-bottom', 'right-top', 'right-bottom'" + "description": "Placement of the popover: One of 'top', 'bottom', 'right', 'left', 'topleft', 'topright', 'bottomleft', 'bottomright', 'lefttop', 'leftbottom', 'righttop', 'rightbottom'" }, { "prop": "fallbackPlacement", diff --git a/src/components/skeleton/index.js b/src/components/skeleton/index.js index 108ecde0a09..9b656621098 100644 --- a/src/components/skeleton/index.js +++ b/src/components/skeleton/index.js @@ -8,11 +8,11 @@ import { BSkeletonWrapper } from './skeleton-wrapper' const SkeletonPlugin = /*#__PURE__*/ pluginFactory({ components: { BSkeleton, - BSkeletonWrapper, - BSkeletonTable, + BSkeletonIcon, BSkeletonImg, - BSkeletonIcon + BSkeletonTable, + BSkeletonWrapper } }) -export { SkeletonPlugin, BSkeleton, BSkeletonWrapper, BSkeletonTable, BSkeletonImg, BSkeletonIcon } +export { SkeletonPlugin, BSkeleton, BSkeletonIcon, BSkeletonImg, BSkeletonTable, BSkeletonWrapper } diff --git a/src/components/tooltip/helpers/bv-tooltip.js b/src/components/tooltip/helpers/bv-tooltip.js index 63f33824aad..28893c2e71b 100644 --- a/src/components/tooltip/helpers/bv-tooltip.js +++ b/src/components/tooltip/helpers/bv-tooltip.js @@ -621,19 +621,31 @@ export const BVTooltip = /*#__PURE__*/ Vue.extend({ } }, fixTitle() { - // If the target has a `title` attribute, remove it and store it on a data attribute + // If the target has a `title` attribute, + // remove it and store it on a data attribute const target = this.getTarget() - if (target && hasAttr(target, 'title')) { - setAttr(target, DATA_TITLE_ATTR, getAttr(target, 'title') || '') + if (hasAttr(target, 'title')) { + // Get `title` attribute value and remove it from target + const title = getAttr(target, 'title') setAttr(target, 'title', '') + // Only set the data attribute when the value is truthy + if (title) { + setAttr(target, DATA_TITLE_ATTR, title) + } } }, restoreTitle() { - // If the target had a title, restore it and remove the data attribute + // If the target had a `title` attribute, + // restore it and remove the data attribute const target = this.getTarget() - if (target && hasAttr(target, DATA_TITLE_ATTR)) { - setAttr(target, 'title', getAttr(target, DATA_TITLE_ATTR) || '') + if (hasAttr(target, DATA_TITLE_ATTR)) { + // Get data attribute value and remove it from target + const title = getAttr(target, DATA_TITLE_ATTR) removeAttr(target, DATA_TITLE_ATTR) + // Only restore the `title` attribute when the value is truthy + if (title) { + setAttr(target, 'title', title) + } } }, // --- BvEvent helpers --- diff --git a/src/components/tooltip/package.json b/src/components/tooltip/package.json index 39e6a214e2c..f03f1674ebe 100644 --- a/src/components/tooltip/package.json +++ b/src/components/tooltip/package.json @@ -25,7 +25,7 @@ }, { "prop": "placement", - "description": "Placement of the tooltip: One of 'top', 'bottom', 'right', 'left', 'top-left', 'top-right', 'bottom-left', 'bottom-right', 'left-top', 'left-bottom', 'right-top', 'right-bottom'" + "description": "Placement of the tooltip: One of 'top', 'bottom', 'right', 'left', 'topleft', 'topright', 'bottomleft', 'bottomright', 'lefttop', 'leftbottom', 'righttop', 'rightbottom'" }, { "prop": "fallbackPlacement", diff --git a/src/components/tooltip/tooltip.spec.js b/src/components/tooltip/tooltip.spec.js index 8818767987f..3235462bd01 100644 --- a/src/components/tooltip/tooltip.spec.js +++ b/src/components/tooltip/tooltip.spec.js @@ -1445,4 +1445,85 @@ describe('b-tooltip', () => { wrapper.destroy() }) + + it('saves title in data attribute on open and adds to back on hide', async () => { + jest.useFakeTimers() + const wrapper = mount(App, { + attachTo: createContainer(), + propsData: { + triggers: 'click', + show: false, + titleAttr: 'bar' + }, + slots: { + default: 'title' + } + }) + + expect(wrapper.vm).toBeDefined() + await waitNT(wrapper.vm) + await waitRAF() + await waitNT(wrapper.vm) + await waitRAF() + jest.runOnlyPendingTimers() + + expect(wrapper.element.tagName).toBe('ARTICLE') + expect(wrapper.attributes('id')).toBeDefined() + expect(wrapper.attributes('id')).toEqual('wrapper') + + // The trigger button + const $button = wrapper.find('button') + expect($button.exists()).toBe(true) + expect($button.attributes('id')).toBeDefined() + expect($button.attributes('id')).toEqual('foo') + expect($button.attributes('title')).toBeDefined() + expect($button.attributes('title')).toEqual('bar') + expect($button.attributes('data-original-title')).not.toBeDefined() + expect($button.attributes('aria-describedby')).not.toBeDefined() + + // Show tooltip + await wrapper.setProps({ show: true }) + + expect($button.attributes('title')).toBeDefined() + expect($button.attributes('title')).toEqual('') + expect($button.attributes('data-original-title')).toBeDefined() + expect($button.attributes('data-original-title')).toEqual('bar') + expect($button.attributes('aria-describedby')).toBeDefined() + // ID of the tooltip that will be in the body + const adb = $button.attributes('aria-describedby') + + // wrapper + const $tipHolder = wrapper.findComponent(BTooltip) + expect($tipHolder.exists()).toBe(true) + expect($tipHolder.element.nodeType).toEqual(Node.COMMENT_NODE) + + // Find the tooltip element in the document + const tip = document.getElementById(adb) + expect(tip).not.toBe(null) + expect(tip).toBeInstanceOf(HTMLElement) + expect(tip.tagName).toEqual('DIV') + expect(tip.classList.contains('tooltip')).toBe(true) + expect(tip.classList.contains('b-tooltip')).toBe(true) + expect(tip.classList.contains('interactive')).toBe(false) + expect(tip.textContent).toEqual('title') + + // Hide the tooltip + await wrapper.setProps({ show: false }) + await waitRAF() + await waitRAF() + jest.runOnlyPendingTimers() + await waitNT(wrapper.vm) + await waitRAF() + + expect($button.attributes('title')).toBeDefined() + expect($button.attributes('title')).toEqual('bar') + expect($button.attributes('data-original-title')).not.toBeDefined() + expect($button.attributes('aria-describedby')).not.toBeDefined() + + // Tooltip element should not be in the document + expect(document.body.contains(tip)).toBe(false) + expect(document.querySelector(adb)).toBe(null) + + wrapper.destroy() + }) }) diff --git a/src/index.js b/src/index.js index 5ab58738dba..c5382db644c 100644 --- a/src/index.js +++ b/src/index.js @@ -285,7 +285,11 @@ export { BSidebar } from './components/sidebar/sidebar' // export * from './components/skeleton' export { SkeletonPlugin } from './components/skeleton' -export { BSkeleton } from './components/skeleton' +export { BSkeleton } from './components/skeleton/skeleton' +export { BSkeletonIcon } from './components/skeleton/skeleton-icon' +export { BSkeletonImg } from './components/skeleton/skeleton-img' +export { BSkeletonTable } from './components/skeleton/skeleton-table' +export { BSkeletonWrapper } from './components/skeleton/skeleton-wrapper' // export * from './components/spinner' export { SpinnerPlugin } from './components/spinner' diff --git a/src/mixins/dropdown.js b/src/mixins/dropdown.js index a23ff245f24..c9fced295c1 100644 --- a/src/mixins/dropdown.js +++ b/src/mixins/dropdown.js @@ -127,8 +127,7 @@ export default { // Position `static` is needed to allow menu to "breakout" of the `scrollParent` // boundaries when boundary is anything other than `scrollParent` // See: https://github.com/twbs/bootstrap/issues/24251#issuecomment-341413786 - const { boundary } = this - return boundary !== 'scrollParent' || !boundary ? 'position-static' : '' + return this.boundary !== 'scrollParent' && !this.inNavbar ? 'position-static' : '' } }, watch: { diff --git a/yarn.lock b/yarn.lock index 5103a375e17..957a2436165 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11146,6 +11146,15 @@ postcss@7.x.x, postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, source-map "^0.6.1" supports-color "^6.1.0" +postcss@^7.0.34: + version "7.0.34" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.34.tgz#f2baf57c36010df7de4009940f21532c16d65c20" + integrity sha512-H/7V2VeNScX9KE83GDrDZNiGT1m2H+UTnlinIzhjlLX9hfMUn1mHNnGeX81a1c8JSBdBvqk7c2ZOG6ZPn5itGw== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -12106,10 +12115,10 @@ rollup-pluginutils@^2.8.1: dependencies: estree-walker "^0.6.1" -rollup@^2.27.0: - version "2.27.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.27.0.tgz#f2b70a8dd583bc3675b36686289aa9a51e27af4f" - integrity sha512-1WlbhNdzhLjdhh2wsf6CDxmuBAYG+5O53fYqCcGv8aJOoX/ymCfCY6oZnvllXZzaC/Ng+lPPwq9EMbHOKc5ozA== +rollup@^2.27.1: + version "2.27.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.27.1.tgz#372744e1d36eba0fd942d997600c2fc2ca266305" + integrity sha512-GiWHQvnmMgBktSpY/1+nrGpwPsTw4b9P28og2uedfeq4JZ16rzAmnQ5Pm/E0/BEmDNia1ZbY7+qu3nBgNa19Hg== optionalDependencies: fsevents "~2.1.2" @@ -13253,10 +13262,10 @@ terser@^4.1.2, terser@^4.3.9, terser@^4.6.12, terser@^4.6.3: source-map "~0.6.1" source-map-support "~0.5.12" -terser@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.1.tgz#f50fe20ab48b15234fe9bdd86b10148ad5fca787" - integrity sha512-yD80f4hdwCWTH5mojzxe1q8bN1oJbsK/vfJGLcPZM/fl+/jItIVNKhFIHqqR71OipFWMLgj3Kc+GIp6CeIqfnA== +terser@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.2.tgz#f4bea90eb92945b2a028ceef79181b9bb586e7af" + integrity sha512-H67sydwBz5jCUA32ZRL319ULu+Su1cAoZnnc+lXnenGRYWyLE3Scgkt8mNoAsMx0h5kdo758zdoS0LG9rYZXDQ== dependencies: commander "^2.20.0" source-map "~0.6.1"