diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 1446f26b4b7..0cf04588611 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -25,7 +25,7 @@ A clear and concise description of what the pull request does. - [ ] It's submitted to the `dev` branch, **not** the `master` branch - [ ] When resolving a specific issue, it's referenced in the PR's title (i.e. `[...] (fixes #xxx[,#xxx])`, where "xxx" is the issue number) - [ ] It should address only one issue or feature. If adding multiple features or fixing a bug and adding a new feature, break them into separate PRs if at all possible. -- [ ] The title should follow the [**Conventional Commits**](https://www.conventionalcommits.org/) naming convention (i.e. `fix(alert): not alerting during SSR render`, `docs(badge): update pill examples, fix typos`, `chore: fix typo in README`, etc). **This is very important, as the `CHANGELOG` is generated from these messages.** +- [ ] The title should follow the [**Conventional Commits**](https://www.conventionalcommits.org/) naming convention (i.e. `fix(alert): not alerting during SSR render`, `docs(badge): update pill examples`, `chore(docs): fix typo in README`, etc). **This is very important, as the `CHANGELOG` is generated from these messages.** **If new features/enhancement/fixes are added or changed:** @@ -33,6 +33,7 @@ A clear and concise description of what the pull request does. - [ ] Includes any needed TypeScript declaration file updates - [ ] New/updated tests are included and passing (if required) - [ ] Existing test suites are passing +- [ ] CodeCov for patch has met target - [ ] The changes have not impacted the functionality of other components or directives - [ ] ARIA Accessibility has been taken into consideration (Does it affect screen reader users or keyboard only users? Clickable items should be in the tab index, etc.) diff --git a/.github/renovate.json b/.github/renovate.json index bba6e15153b..9e03220db5a 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -13,6 +13,10 @@ { "packageNames": ["prettier"], "allowedVersions": "<=1.14.3" + }, + { + "packageNames": ["@vue/test-utils"], + "allowedVersions": "<=1.0.0-beta.29" } ] } diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f3f791f5c7..c1785cad9f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,86 @@ > [standard-version](https://github.com/conventional-changelog/standard-version) for commit > guidelines. + + +## [v2.2.0](https://github.com/bootstrap-vue/bootstrap-vue/compare/v2.1.0...v2.2.0) + +Released: 2020-01-08 + +### Overview v2.2.0 + +- New optional icon components based on `BootstrapIcons v1.0.0-alpha2` +- New tagged input component `` +- Support for `Bootstrap v4.4.1` CSS/SCSS + +### Features v2.2.0 + +- **icons:** new optional icon components + ([#4489](https://github.com/bootstrap-vue/bootstrap-vue/issues/4489)) + ([d2bef17](https://github.com/bootstrap-vue/bootstrap-vue/commit/d2bef1715636fcb83de6d51808683e6feda671d0)) +- **b-collapse:** add new prop `appear` to animate an initially visible collapse + ([#4317](https://github.com/bootstrap-vue/bootstrap-vue/issues/4317)) + ([136a72b](https://github.com/bootstrap-vue/bootstrap-vue/commit/136a72b0352d4bb1339ab31f791087cbcda42fa5)) +- **b-collapse:** add optional scoping to default slot + ([#4405](https://github.com/bootstrap-vue/bootstrap-vue/issues/4405)) + ([8e95bac](https://github.com/bootstrap-vue/bootstrap-vue/commit/8e95bacf9d00562f2676689d067ae0db009cbbb6)) +- **b-container:** add support for bootstrap v4.4.x new responsive containers + ([0e318f4](https://github.com/bootstrap-vue/bootstrap-vue/commit/0e318f4755e65eb569dcc579938d0d72c02abd62)) +- **b-dropdown:** add splitClass property to dropdown component + ([#4394](https://github.com/bootstrap-vue/bootstrap-vue/issues/4394)) + ([a5f342e](https://github.com/bootstrap-vue/bootstrap-vue/commit/a5f342e0e4de2186259e36e42cecda8c20e1c8ab)) +- **b-dropdown-form:** new `form-class` prop for adding classes to the form element (closes + [#4474](https://github.com/bootstrap-vue/bootstrap-vue/issues/4474)) + ([#4475](https://github.com/bootstrap-vue/bootstrap-vue/issues/4475)) + ([eef4200](https://github.com/bootstrap-vue/bootstrap-vue/commit/eef4200976f7921b1bb03f50c0ece8ee7c41ed0e)) +- **b-form-select:** add group/tree support and dedicated option and option-group components (closes + [#3222](https://github.com/bootstrap-vue/bootstrap-vue/issues/3222)) + ([#4267](https://github.com/bootstrap-vue/bootstrap-vue/issues/4267)) + ([f1ed017](https://github.com/bootstrap-vue/bootstrap-vue/commit/f1ed0177c20f9d7e7e340a8815d1b6bc66f7cb76)) +- **b-form-select:** support paths for `valueField`, `textField`, `htmlField` and `disabledField` + props ([#4386](https://github.com/bootstrap-vue/bootstrap-vue/issues/4386)) + ([ed3b736](https://github.com/bootstrap-vue/bootstrap-vue/commit/ed3b7360af415dc3cc56f0b6662c9d48cc165781)) +- **b-form-tags:** new tagged input component + ([#4409](https://github.com/bootstrap-vue/bootstrap-vue/issues/4409)) + ([00eb9d9](https://github.com/bootstrap-vue/bootstrap-vue/commit/00eb9d9fd460adca8227b3b344284b5cc49a734f)) +- **b-row:** add Bootstrap v4.4 row columns support + ([#4439](https://github.com/bootstrap-vue/bootstrap-vue/issues/4439)) + ([833b028](https://github.com/bootstrap-vue/bootstrap-vue/commit/833b028a2d6101d01b7012a7378359db1c801695)) +- **b-table:** better sort labeling for screen readers (closes + [#4487](https://github.com/bootstrap-vue/bootstrap-vue/issues/4487)) + ([#4488](https://github.com/bootstrap-vue/bootstrap-vue/issues/4488)) + ([d4e66fa](https://github.com/bootstrap-vue/bootstrap-vue/commit/d4e66fa48fdd1cd7fd4b93907fe999de3fc577f8)) +- **b-table, b-table-lite:** new `tbody-tr-attr` prop for arbitrary row attributes (closes + [#1864](https://github.com/bootstrap-vue/bootstrap-vue/issues/1864)) + ([#4481](https://github.com/bootstrap-vue/bootstrap-vue/issues/4481)) + ([4acf6ed](https://github.com/bootstrap-vue/bootstrap-vue/commit/4acf6ed863dd5edd85897a01b099c42322097d1b)) +- **b-tooltip:** add `noninteractive` prop (closes + [#4556](https://github.com/bootstrap-vue/bootstrap-vue/issues/4556)) + ([#4563](https://github.com/bootstrap-vue/bootstrap-vue/issues/4563)) + ([b3ad726](https://github.com/bootstrap-vue/bootstrap-vue/commit/b3ad7264d9b10fb1b8dfba70c62eed11a56519d6)) +- **build:** configure pre-commit hook (closes + [#4532](https://github.com/bootstrap-vue/bootstrap-vue/issues/4532)) + ([#4552](https://github.com/bootstrap-vue/bootstrap-vue/issues/4552)) + ([1bf9e59](https://github.com/bootstrap-vue/bootstrap-vue/commit/1bf9e59e8888a7a2cd6f135665103419f603a32d)) + +### Bug Fixes v2.2.0 + +- **b-table, b-table-lite:** handle edge case with row events when table is removed from dom. + instantiate row event handlers only when listeners are registered (fixes + [#4384](https://github.com/bootstrap-vue/bootstrap-vue/issues/4384)) + ([#4388](https://github.com/bootstrap-vue/bootstrap-vue/issues/4388)) + ([9a81cd4](https://github.com/bootstrap-vue/bootstrap-vue/commit/9a81cd414a2c534b96de0d82c3d00d94651e5a7b)) +- **b-toast:** fix interal `ensureToaster` method call when toaster name changes + ([#4468](https://github.com/bootstrap-vue/bootstrap-vue/issues/4468)) + ([744bb7a](https://github.com/bootstrap-vue/bootstrap-vue/commit/744bb7a77092a04184af31bf285e432110e1ab44)) +- **tooltips, popovers:** fix memory leak (closes + [#4400](https://github.com/bootstrap-vue/bootstrap-vue/issues/4400)) + ([#4401](https://github.com/bootstrap-vue/bootstrap-vue/issues/4401)) + ([c71352d](https://github.com/bootstrap-vue/bootstrap-vue/commit/c71352d674347e5e2d72fe8b82334fc87a4ffd8c)) +- **docs:** handle undocumented breaking changes in babel-standalone for IE11 + ([#4484](https://github.com/bootstrap-vue/bootstrap-vue/issues/4484)) + ([56f8bb5](https://github.com/bootstrap-vue/bootstrap-vue/commit/56f8bb5af7fb7188da035210e8be28d7ae1c7bc1)) + ## [v2.1.0](https://github.com/bootstrap-vue/bootstrap-vue/compare/v2.0.4...v2.1.0) @@ -77,8 +157,8 @@ Released: 2019-11-12 ([#4325](https://github.com/bootstrap-vue/bootstrap-vue/issues/4325)) ([c686088](https://github.com/bootstrap-vue/bootstrap-vue/commit/c686088)) - **b-table, b-table-lite, b-table-simple:** fix issue with sticky columns when table is not - responsive but has sticky headers - (fixes [#4354](https://github.com/bootstrap-vue/bootstrap-vue/issues/4354)) + responsive but has sticky headers (fixes + [#4354](https://github.com/bootstrap-vue/bootstrap-vue/issues/4354)) ([#4356](https://github.com/bootstrap-vue/bootstrap-vue/issues/4356)) ([56b3958](https://github.com/bootstrap-vue/bootstrap-vue/commit/56b3958)) - **b-table, b-table-lite, b-tbody:** fix delegated event handlers when transition + minor diff --git a/LICENSE b/LICENSE index 0afe512d48d..80d672f72fb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016-2019 - BootstrapVue +Copyright (c) 2016-2020 - BootstrapVue Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/docs/components/anchored-heading.js b/docs/components/anchored-heading.js index 71933ed108a..be467ae0348 100644 --- a/docs/components/anchored-heading.js +++ b/docs/components/anchored-heading.js @@ -26,6 +26,7 @@ export default { return h( `h${props.level}`, mergeData(data, { + staticClass: 'bv-no-focus-ring', attrs: { id: props.id, tabindex: '-1' diff --git a/docs/components/componentdoc.vue b/docs/components/componentdoc.vue index f528fc68bab..c797ddd24b9 100644 --- a/docs/components/componentdoc.vue +++ b/docs/components/componentdoc.vue @@ -2,9 +2,10 @@
- + {{ tag }} + v{{ version }}+ - + View source @@ -120,7 +127,7 @@

Caution: Props that support HTML strings - (*-html) can be vulerable to + (*-html) can be vulnerable to Cross Site Scripting (XSS) attacks @@ -310,18 +317,23 @@ ul.component-ref-mini-toc:empty { diff --git a/docs/components/importdoc.vue b/docs/components/importdoc.vue index c246369c47b..19f1185e03b 100644 --- a/docs/components/importdoc.vue +++ b/docs/components/importdoc.vue @@ -118,9 +118,9 @@ + + + ``` ## Build variants @@ -643,12 +702,29 @@ JavaScript files. Choosing the best variant for your build environment / packager helps reduce bundle sizes. If your bundler supports esm modules, it will automatically prefer it over commonjs. -| Variant | Environments | Package path | -| -------------- | ---------------------- | ---------------------------------------------------------------------- | -| **ESM module** | webpack 2+ / rollup.js | `esm/index.js` | -| ESM bundle | webpack 2+ / rollup.js | `dist/bootstrap-vue.esm.js` | -| commonjs2 | webpack 1 / ... | `dist/bootstrap-vue.common.js` _or_ `dist/bootstrap-vue.common.min.js` | -| UMD | Browser | `dist/bootstrap-vue.js` _or_ `dist/bootstrap-vue.min.js` | +| Variant | Environments | Tree Shake | Package path | +| -------------- | ---------------------- | ---------- | ---------------------------------------------------------------------- | +| **ESM module** | webpack 2+ / rollup.js | Yes | `esm/index.js` | +| ESM bundle | webpack 2+ / rollup.js | Yes | `dist/bootstrap-vue.esm.js` | +| commonjs2 | webpack 1 / ... | No | `dist/bootstrap-vue.common.js` _or_ `dist/bootstrap-vue.common.min.js` | +| UMD | Browser | No | `dist/bootstrap-vue.js` _or_ `dist/bootstrap-vue.min.js` | + +Note the UMD (browser) variant **does not** include BootstrapVue [icons](/docs/icons) support. All +other variants listed above _do include_ the `BootstrapVueIcons` (`IconsPlugin`) plugin (note the +icons plugin is not automatically installed, and must explicitly installed via `Vue.use()`. See the +[Icons usage](/docs/icons#usage) section for more details. + +Icons only modules: + +| Variant | Environments | Tree Shake | Package path | +| -------------- | ---------------------- | ---------- | ---------------------------------------------------------------------------------- | +| **ESM bundle** | webpack 2+ / rollup.js | Yes | `dist/bootstrap-vue-icons.esm.js` | +| commonjs2 | webpack 1 / ... | No | `dist/bootstrap-vue-icons.common.js` _or_ `dist/bootstrap-vue-icons.common.min.js` | +| UMD | Browser | No | `dist/bootstrap-vue-icons.js` _or_ `dist/bootstrap-vue-icons.min.js` | + +The `ESM` module build and the `ESM` bundles (single file) are +[tree-shakeable](#tree-shaking-with-module-bundlers), but you will experience smaller final bundle +sizes when using the `ESM` module _vs._ the `ESM` bundle. All of the build variants listed above have been pre-transpiled targeting the [browsers](https://github.com/bootstrap-vue/bootstrap-vue/blob/master/.browserslistrc) supported by @@ -659,21 +735,18 @@ reduce final project bundle sizes. See the [Using BootstrapVue source code for smaller bundles](#using-bootstrapvue-source-code-for-smaller-bundles) section above for more details. -Both the `ESM` module and `ESM` bundle (single file) are -[tree-shakeable](#tree-shaking-with-module-bundlers), but you will experience smaller final bundle -sizes when using the `ESM` module _vs._ the `ESM` bundle. - ### Dependencies BootstrapVue relies on `Popper.js` (for Tooltip, Popover, and Dropdown positioning), `PortalVue` (for toasts) and [`vue-functional-data-merge`](https://github.com/alexsasharegan/vue-functional-data-merge) (used by -our functional components). These three dependencies are included in the `UMD` bundle. +our functional components). These three dependencies are included in the BootstrapVue `UMD` bundle, +while the UMD (browser) icons only bundle includes `vue-functional-data-merge`. ## Migrating a project already using Bootstrap -If you've already been using Bootstrap v4, there are a couple adjustments you may need to make to -your project: +If you've already been using Bootstrap vv{{bootstrapVersionMajor}}, there are a couple adjustments +you may need to make to your project: - Remove the `bootstrap.js` file from your page scripts or build pipeline - If Bootstrap is the only thing relying on `jQuery`, you can safely remove it — BootstrapVue **does @@ -685,9 +758,9 @@ your project: ### CSS -BootstrapVue is to be used with Bootstrap v4.3 CSS/SCSS. Please see -[Browsers and devices](https://getbootstrap.com/docs/4.3/getting-started/browsers-devices) for more -information about browsers currently supported by Bootstrap v4. +BootstrapVue is to be used with Bootstrap v{{bootstrapVersionMinor}} CSS/SCSS. Please see +Browsers and devices for more +information about browsers currently supported by Bootstrap v{{bootstrapVersionMajor}}. ### JS diff --git a/docs/nuxt.config.js b/docs/nuxt.config.js index 0892e5e1919..2e98febdeb2 100644 --- a/docs/nuxt.config.js +++ b/docs/nuxt.config.js @@ -88,8 +88,8 @@ renderer.heading = function(text, level, raw, slugger) { ANCHOR_LINK_HEADING_LEVELS.indexOf(level) !== -1 ? `` : '' - - return `${getTextMarkup(text + anchor)}\n` + const attrs = `id="${link}" class="bv-no-focus-ring"` + return `${getTextMarkup(text + anchor)}\n` } // Convert lead-in blockquote paragraphs to true bootstrap docs leads diff --git a/docs/pages/docs/components/_slug.js b/docs/pages/docs/components/_slug.js index 3dd9b6cc125..36110b9dbc5 100644 --- a/docs/pages/docs/components/_slug.js +++ b/docs/pages/docs/components/_slug.js @@ -33,9 +33,9 @@ export default { h(AnchoredHeading, { props: { id: 'component-reference' } }, 'Component reference'), // Component reference information ...this.meta.components.map( - ({ component, events, rootEventListeners, slots, aliases, props: propsMeta }) => + ({ component, events, rootEventListeners, slots, aliases, props: propsMeta, version }) => h(Componentdoc, { - props: { component, events, rootEventListeners, slots, aliases, propsMeta } + props: { component, events, rootEventListeners, slots, aliases, propsMeta, version } }) ), // Component importing information diff --git a/docs/pages/docs/icons/index.js b/docs/pages/docs/icons/index.js new file mode 100644 index 00000000000..3f001e38091 --- /dev/null +++ b/docs/pages/docs/icons/index.js @@ -0,0 +1,75 @@ +import AnchoredHeading from '~/components/anchored-heading' +import Componentdoc from '~/components/componentdoc' +import IconsTable from '~/components/icons-table' +import Importdoc from '~/components/importdoc' +import Main from '~/components/main' +import Section from '~/components/section' +import docsMixin from '~/plugins/docs-mixin' +import { icons as iconsMeta, bootstrapIconsVersion } from '~/content' +import readme from '~/../src/icons/README.md' + +export default { + name: 'BDVIcons', + layout: 'docs', + // We use a string template here so that the docs README can do interpolation + template: ` +

+
${readme}
+
+ Component reference + +
+

+ Individual icon components are not listed here due to the large number of components. +

+
+ +

+ IconsPlugin is also exported as BootstrapVueIcons. +

+
+
`, + components: { + AnchoredHeading, + Componentdoc, + IconsTable, + Importdoc, + Main, + Section + }, + mixins: [docsMixin], + data() { + return { + readme: readme, + // Key for icons meta is '' (empty slug) + meta: iconsMeta[''], + bootstrapIconsVersion + } + }, + computed: { + componentMeta() { + // `docs/content/index.js` massages the list of icon components + // to include only `BIcon` and an example component + const components = this.meta.components + // Add in a special property or grabbing the component props + // as `BIcon{IconName}` doesn't exist + components[1].srcComponent = 'BIconBlank' + return components + }, + importMeta() { + return { ...this.meta, slug: 'icons', components: this.componentMeta } + } + } +} diff --git a/docs/pages/docs/index.js b/docs/pages/docs/index.js index 92f5797a4bc..ace02436f32 100644 --- a/docs/pages/docs/index.js +++ b/docs/pages/docs/index.js @@ -46,6 +46,10 @@ export default { } }, computed: { + hrefBootstrapBrowserDevices() { + const minorVersion = this.bootstrapVersionMinor + return `//getbootstrap.com/docs/${minorVersion}/getting-started/browsers-devices` + }, // TODO: pull this from the meta.json file meta() { return { diff --git a/docs/pages/index.vue b/docs/pages/index.vue index 0903aa8c95a..3520d945c18 100644 --- a/docs/pages/index.vue +++ b/docs/pages/index.vue @@ -132,7 +132,7 @@

- With over 40 available plugins and more than 80 custom UI components, + With over 40 available plugins and more than 80 custom UI components, directives, and over 300 icons, BootstrapVue provides one of the most comprehensive implementations of the Bootstrap v{{ bootstrapVersionMinor }} component and grid system @@ -190,28 +190,9 @@ - - + Responsive - Mobile first responsive layout @@ -219,29 +200,9 @@ - - + Modular - Import only the features that you need @@ -249,27 +210,15 @@ - - + > Accessible - Automated WAI-ARIA accessibility markup @@ -293,10 +242,8 @@ d="M356.9 64.3H280l-56 88.6-48-88.6H0L224 448 448 64.3h-91.1zm-301.2 32h53.8L224 294.5 338.4 96.3h53.8L224 384.5 55.7 96.3z" /> - Modern - Built with Vue.js v{{ vueVersionMinor }} and Bootstrap SCSS v{{ bootstrapVersionMinor }} @@ -305,34 +252,9 @@ - - + Configurable - Create themes with SCSS variables and global options @@ -354,10 +276,8 @@ d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z" /> - Free - Open sourced on GitHub, MIT License diff --git a/docs/pages/play.vue b/docs/pages/play.vue index 1530d043213..cde2f93273a 100644 --- a/docs/pages/play.vue +++ b/docs/pages/play.vue @@ -685,7 +685,6 @@ export default { } else { delete options.template } - // Vue's `errorCapture` doesn't always handle errors in methods (although it // does if the method is used as a `v-on`/`@` handler), so we wrap any methods // with a try/catch handler so we can show the error in our GUI log console. @@ -709,15 +708,15 @@ export default { }) } - // Try and buld the user app + // Try and build the user app try { const holder = document.createElement('div') this.$refs.result.appendChild(holder) this.playVM = new Vue({ ...options, el: holder, - // Router needed for tooltips/popovers so they hide when - // docs route changes + // Router needed for tooltips/popovers/toasts so + // that they hide when docs route changes router: this.$router, // We set a fake parent so we can capture most runtime and // render errors (this is an error boundary component) @@ -751,7 +750,7 @@ export default { this.compiledJs = null return } - const js = this.js.trim() || '{}' + const js = (this.js || '').trim() || '{}' this.compiling = true let compiled = null this.$nextTick(() => { @@ -759,7 +758,7 @@ export default { try { // The app build process expects the app options to // be assigned to the `options` variable - compiled = this.compiler(`;options = ${js};`) + compiled = this.compiler(';options = ' + js + ';') } catch (err) { this.errHandler(err, 'javascript') window.console.error('Error in javascript', err) diff --git a/docs/plugins/bootstrap-vue.js b/docs/plugins/bootstrap-vue.js index 4b3ae0e1349..eccf73126ff 100644 --- a/docs/plugins/bootstrap-vue.js +++ b/docs/plugins/bootstrap-vue.js @@ -1,4 +1,5 @@ import Vue from 'vue' -import BootstrapVue from '../../src' +import { BootstrapVue, BootstrapVueIcons } from '../../src' Vue.use(BootstrapVue) +Vue.use(BootstrapVueIcons) diff --git a/docs/utils/compile-js.js b/docs/utils/compile-js.js index d5d13e86850..b7373381a4f 100644 --- a/docs/utils/compile-js.js +++ b/docs/utils/compile-js.js @@ -1,12 +1,61 @@ -// Utility for tranpiling ES6 code into ES5 for playground and v-play +// Utility for transpiling ES6 code into ES5 for playground and `v-play` +// Imported only on demand when needed import { transform, disableScriptTags } from '@babel/standalone' +// Babel broke the standalone version via PR https://github.com/babel/babel/pull/10420 +// Which assumes the browser supports String.prototype.trimLeft/Right +// IE 11 does not support either, and polyfill.io does not polyfill them +// So we do it here (as this file is only loaded if we need transpilation): +if (typeof window !== 'undefined') { + const Proto = window.String.prototype + + // Ensure we have a `trimStart` method + ;((obj, prop) => { + if (!(prop in obj && obj[prop])) { + const rx = /^\s+/ + obj[prop] = + obj.trimLeft || + function() { + return this.replace(rx, '') + } + } + })(Proto, 'trimStart') + + // Ensure we have a `trimLeft` method + ;((obj, prop) => { + if (!(prop in obj && obj[prop])) { + obj[prop] = obj.trimStart + } + })(Proto, 'trimLeft') + + // Ensure we have a `trimEnd` method + ;((obj, prop) => { + if (!(prop in obj && obj[prop])) { + const rx = /\s+$/ + obj[prop] = + obj.trimRight || + function() { + return this.replace(rx, '') + } + } + })(Proto, 'trimEnd') + + // Ensure we have a `trimRight` method + ;((obj, prop) => { + if (!(prop in obj && obj[prop])) { + obj[prop] = obj.trimEnd + } + })(Proto, 'trimRight') +} + +// Prevent Babel/Standalone from processing + + +``` ## Inline and stacked checkboxes diff --git a/src/components/form-checkbox/_form-checkbox-group.scss b/src/components/form-checkbox/_form-checkbox-group.scss index 95f35ac44ff..a65e471525b 100644 --- a/src/components/form-checkbox/_form-checkbox-group.scss +++ b/src/components/form-checkbox/_form-checkbox-group.scss @@ -1 +1,2 @@ +@import "../../utilities"; @import "../input-group/index"; diff --git a/src/components/form-checkbox/form-checkbox-group.spec.js b/src/components/form-checkbox/form-checkbox-group.spec.js index 2c9e71a3500..17fe440aca7 100644 --- a/src/components/form-checkbox/form-checkbox-group.spec.js +++ b/src/components/form-checkbox/form-checkbox-group.spec.js @@ -17,9 +17,10 @@ describe('form-checkbox-group', () => { wrapper.destroy() }) - it('default has no classes on wrapper', async () => { + it('default has no classes on wrapper other than focus ring', async () => { const wrapper = mount(BFormCheckboxGroup) - expect(wrapper.classes().length).toEqual(0) + expect(wrapper.classes()).toContain('bv-no-focus-ring') + expect(wrapper.classes().length).toEqual(1) wrapper.destroy() }) @@ -177,9 +178,10 @@ describe('form-checkbox-group', () => { } }) expect(wrapper.classes()).toBeDefined() - expect(wrapper.classes().length).toBe(2) + expect(wrapper.classes().length).toBe(3) expect(wrapper.classes()).toContain('btn-group') expect(wrapper.classes()).toContain('btn-group-toggle') + expect(wrapper.classes()).toContain('bv-no-focus-ring') wrapper.destroy() }) @@ -193,9 +195,10 @@ describe('form-checkbox-group', () => { } }) expect(wrapper.classes()).toBeDefined() - expect(wrapper.classes().length).toBe(2) + expect(wrapper.classes().length).toBe(3) expect(wrapper.classes()).toContain('btn-group-vertical') expect(wrapper.classes()).toContain('btn-group-toggle') + expect(wrapper.classes()).toContain('bv-no-focus-ring') wrapper.destroy() }) @@ -209,10 +212,11 @@ describe('form-checkbox-group', () => { } }) expect(wrapper.classes()).toBeDefined() - expect(wrapper.classes().length).toBe(3) + expect(wrapper.classes().length).toBe(4) expect(wrapper.classes()).toContain('btn-group') expect(wrapper.classes()).toContain('btn-group-toggle') expect(wrapper.classes()).toContain('btn-group-lg') + expect(wrapper.classes()).toContain('bv-no-focus-ring') wrapper.destroy() }) @@ -227,10 +231,11 @@ describe('form-checkbox-group', () => { } }) expect(wrapper.classes()).toBeDefined() - expect(wrapper.classes().length).toBe(3) + expect(wrapper.classes().length).toBe(4) expect(wrapper.classes()).toContain('btn-group-vertical') expect(wrapper.classes()).toContain('btn-group-toggle') expect(wrapper.classes()).toContain('btn-group-lg') + expect(wrapper.classes()).toContain('bv-no-focus-ring') wrapper.destroy() }) diff --git a/src/components/form-file/form-file.js b/src/components/form-file/form-file.js index 626e35eb104..367374b3a56 100644 --- a/src/components/form-file/form-file.js +++ b/src/components/form-file/form-file.js @@ -1,8 +1,10 @@ import Vue from '../../utils/vue' +import identity from '../../utils/identity' import { from as arrayFrom, isArray, concat } from '../../utils/array' import { getComponentConfig } from '../../utils/config' import { isFile, isFunction, isUndefinedOrNull } from '../../utils/inspect' import { File } from '../../utils/safe-types' +import { toString } from '../../utils/string' import { warn } from '../../utils/warn' import formCustomMixin from '../../mixins/form-custom' import formMixin from '../../mixins/form' @@ -12,6 +14,9 @@ import normalizeSlotMixin from '../../mixins/normalize-slot' const NAME = 'BFormFile' +const VALUE_EMPTY_DEPRECATED_MSG = + 'Setting "value"/"v-model" to an empty string for reset is deprecated. Set to "null" instead.' + // @vue/component export const BFormFile = /*#__PURE__*/ Vue.extend({ name: NAME, @@ -32,9 +37,7 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({ validator: val => { /* istanbul ignore next */ if (val === '') { - warn( - `${NAME} - setting value/v-model to an empty string for reset is deprecated. Set to 'null' instead` - ) + warn(VALUE_EMPTY_DEPRECATED_MSG, NAME) return true } return ( @@ -106,7 +109,7 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({ } // Convert selectedFile to an array (if not already one) - const files = concat(this.selectedFile).filter(Boolean) + const files = concat(this.selectedFile).filter(identity) if (this.hasNormalizedSlot('file-name')) { // There is a slot for formatting the files/names @@ -119,7 +122,7 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({ } else { // Use the user supplied formatter, or the built in one. return isFunction(this.fileNameFormatter) - ? String(this.fileNameFormatter(files)) + ? toString(this.fileNameFormatter(files)) : files.map(file => file.name).join(', ') } } @@ -329,7 +332,7 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({ class: [ this.stateClass, { - [`b-custom-control-${this.size}`]: Boolean(this.size) + [`b-custom-control-${this.size}`]: this.size } ], attrs: { id: this.safeId('_BV_file_outer_') }, diff --git a/src/components/form-file/package.json b/src/components/form-file/package.json index c46d78e43be..82d7a02892c 100644 --- a/src/components/form-file/package.json +++ b/src/components/form-file/package.json @@ -60,7 +60,7 @@ { "prop": "files", "type": "Array", - "description": "Array if File objects" + "description": "Array of File objects" }, { "prop": "names", diff --git a/src/components/form-group/form-group.js b/src/components/form-group/form-group.js index d2ec1d745fd..be2b47ee57c 100644 --- a/src/components/form-group/form-group.js +++ b/src/components/form-group/form-group.js @@ -1,12 +1,12 @@ // Utils import memoize from '../../utils/memoize' -import upperFirst from '../../utils/upper-first' import { arrayIncludes } from '../../utils/array' import { getBreakpointsUpCached } from '../../utils/config' import { select, selectAll, isVisible, setAttr, removeAttr, getAttr } from '../../utils/dom' import { isBrowser } from '../../utils/env' import { isBoolean } from '../../utils/inspect' import { keys, create } from '../../utils/object' +import { upperFirst } from '../../utils/string' // Mixins import formStateMixin from '../../mixins/form-state' import idMixin from '../../mixins/id' @@ -126,6 +126,8 @@ const renderLabel = (h, ctx) => { tabindex: isLegend ? '-1' : null }, class: [ + // Hide the focus ring on the legend + isLegend ? 'bv-no-focus-ring' : '', // When horizontal or if a legend is rendered, add col-form-label // for correct sizing as Bootstrap has inconsistent font styling // for legend in non-horizontal form-groups. @@ -362,7 +364,9 @@ export const BFormGroup = { const inputs = selectAll(SELECTOR, this.$refs.content).filter(isVisible) if (inputs && inputs.length === 1 && inputs[0].focus) { // if only a single input, focus it, emulating label behaviour - inputs[0].focus() + try { + inputs[0].focus() + } catch {} } }, setInputDescribedBy(add, remove) { @@ -405,6 +409,8 @@ export const BFormGroup = { isHorizontal ? BCol : 'div', { ref: 'content', + // Hide focus ring + staticClass: 'bv-no-focus-ring', attrs: { tabindex: isFieldset ? '-1' : null, role: isFieldset ? 'group' : null diff --git a/src/components/form-group/form-group.spec.js b/src/components/form-group/form-group.spec.js index 1da10e729ec..2e4b04b1769 100644 --- a/src/components/form-group/form-group.spec.js +++ b/src/components/form-group/form-group.spec.js @@ -118,7 +118,8 @@ describe('form-group', () => { expect(wrapper.find('label').attributes('for')).toBeDefined() expect(wrapper.find('label').attributes('for')).toEqual('input-id') expect(wrapper.find('div > div').exists()).toBe(true) - expect(wrapper.find('div > div').classes().length).toBe(0) + expect(wrapper.find('div > div').classes()).toContain('bv-no-focus-ring') + expect(wrapper.find('div > div').classes().length).toBe(1) expect(wrapper.find('div > div').attributes('role')).not.toBeDefined() expect(wrapper.find('div > div').attributes('tabindex')).not.toBeDefined() expect(wrapper.find('div > div > input').exists()).toBe(true) @@ -169,7 +170,8 @@ describe('form-group', () => { expect(wrapper.find('label').text()).toEqual('test') expect(wrapper.find('div > div').exists()).toBe(true) expect(wrapper.find('div > div').classes()).toContain('col') - expect(wrapper.find('div > div').classes().length).toBe(1) + expect(wrapper.find('div > div').classes()).toContain('bv-no-focus-ring') + expect(wrapper.find('div > div').classes().length).toBe(2) wrapper.destroy() }) @@ -208,11 +210,13 @@ describe('form-group', () => { expect(wrapper.find('legend').classes()).toContain('col-md-3') expect(wrapper.find('legend').classes()).toContain('col-lg-4') expect(wrapper.find('legend').classes()).toContain('col-xl-5') - expect(wrapper.find('legend').classes().length).toBe(6) + expect(wrapper.find('legend').classes()).toContain('bv-no-focus-ring') + expect(wrapper.find('legend').classes().length).toBe(7) expect(wrapper.find('legend').text()).toEqual('test') expect(wrapper.find('fieldset > div > div').exists()).toBe(true) expect(wrapper.find('fieldset > div > div').classes()).toContain('col') - expect(wrapper.find('fieldset > div > div').classes().length).toBe(1) + expect(wrapper.find('fieldset > div > div').classes()).toContain('bv-no-focus-ring') + expect(wrapper.find('fieldset > div > div').classes().length).toBe(2) expect(wrapper.find('fieldset > div > div').attributes('role')).toEqual('group') expect(wrapper.find('fieldset > div > div').attributes('tabindex')).toEqual('-1') diff --git a/src/components/form-input/README.md b/src/components/form-input/README.md index 34ddf6c1807..aacf4505949 100644 --- a/src/components/form-input/README.md +++ b/src/components/form-input/README.md @@ -75,7 +75,7 @@ rendered and a console warning will be issued. **Caveats with input types:** - Not all browsers support all input types, nor do some types render in the same format across - browser types/versions. + browser types/versions. Refer to [caniuse.com](https://caniuse.com/#search=input). - Browsers that do not support a particular type will fall back to a `text` input type (even though the rendered `type` attribute markup shows the requested type). - No testing is performed to see if the requested input type is supported by the browser. @@ -90,7 +90,7 @@ rendered and a console warning will be issued. prop instead. - `v-model` modifiers `.number` and `.trim` can cause unexpected cursor jumps when the user is typing (this is a Vue issue with `v-model` on custom components). _Avoid using these modifiers_. - Use the `number` or `trip` props instead. + Use the `number` or `trim` props instead. - Older version of Firefox may not support `readonly` for `range` type inputs. - Input types that do not support `min`, `max` and `step` (i.e. `text`, `password`, `tel`, `email`, `url`, etc) will silently ignore these values (although they will still be rendered on the input diff --git a/src/components/form-radio/README.md b/src/components/form-radio/README.md index 4467e5d9d12..de1913d896f 100644 --- a/src/components/form-radio/README.md +++ b/src/components/form-radio/README.md @@ -130,8 +130,124 @@ To have them appear _above_ the inputs generated by `options`, place them in the ## Radio group options array -Please see the [`` `options` prop](/docs/components/form-select#options-property) -docs for details on the formats and helper props associated with `options`. +`options` can be an array of strings or objects. Available fields: + +- **`value`** The selected value which will be set on `v-model` +- **`disabled`** Disables item for selection +- **`text`** Display text, or **`html`** Display basic inline html + +`value` can be a string, number, or simple object. Avoid using complex types in values. + +If both `html` and `text` are provided, `html` will take precedence. Only basic/native HTML is +supported in the `html` field (components will not work). Note that not all browsers will render +inline html (i.e. ``, ``, etc) inside `