wvui (main)

sourcepatches
$ date
Fri Jun 11 03:28:45 UTC 2021

$ git clone file:///srv/git/wvui.git repo --depth=1 -b master
Cloning into 'repo'...

$ git config user.name libraryupgrader

$ git config user.email tools.libraryupgrader@tools.wmflabs.org

$ git submodule update --init

$ grr init
Installed commit-msg hook.

$ git show-ref refs/heads/master
05b684015aeb1b53b9dd74ba84ced2d91bee79c7 refs/heads/master

Upgrading n:eslint from 7.9.0 -> 7.16.0
Upgrading n:eslint-config-wikimedia from 0.17.0 -> 0.20.0
$ npm install

> deasync@0.1.21 install /src/repo/node_modules/deasync
> node ./build.js

`linux-x64-node-10` exists; testing
Binary is fine; exiting

> iltorb@2.4.5 install /src/repo/node_modules/iltorb
> node ./scripts/install.js || node-gyp rebuild

info looking for cached prebuild @ /cache/_prebuilds/2a34dd-iltorb-v2.4.5-node-v64-linux-x64.tar.gz
info found cached prebuild 
info unpacking @ /cache/_prebuilds/2a34dd-iltorb-v2.4.5-node-v64-linux-x64.tar.gz
info unpack resolved to /src/repo/node_modules/iltorb/build/bindings/iltorb.node
info unpack required /src/repo/node_modules/iltorb/build/bindings/iltorb.node successfully
info install Successfully installed iltorb binary!

> pre-commit@1.2.2 install /src/repo/node_modules/pre-commit
> node install.js


> core-js@2.6.12 postinstall /src/repo/node_modules/babel-register/node_modules/core-js
> node -e "try{require('./postinstall')}catch(e){}"

Thank you for using core-js ( https://github.com/zloirock/core-js ) for polyfilling JavaScript standard library!

The project needs your help! Please consider supporting of core-js on Open Collective or Patreon: 
> https://opencollective.com/core-js 
> https://www.patreon.com/zloirock 

Also, the author of core-js ( https://github.com/zloirock ) is looking for a good job -)


> core-js@2.6.12 postinstall /src/repo/node_modules/babel-runtime/node_modules/core-js
> node -e "try{require('./postinstall')}catch(e){}"


> core-js@3.11.3 postinstall /src/repo/node_modules/core-js
> node -e "try{require('./postinstall')}catch(e){}"


> core-js-pure@3.11.3 postinstall /src/repo/node_modules/core-js-pure
> node -e "try{require('./postinstall')}catch(e){}"


> spawn-sync@1.0.15 postinstall /src/repo/node_modules/spawn-sync
> node postinstall


> ejs@2.7.4 postinstall /src/repo/node_modules/webpack-bundle-analyzer/node_modules/ejs
> node ./postinstall.js

Thank you for installing EJS: built with the Jake JavaScript build tool (https://jakejs.com/)


> @wikimedia/wvui@0.2.0 prepare /src/repo
> husky install

husky - Git hooks installed
npm WARN notsup Unsupported engine for eslint-plugin-wdio@7.4.2: wanted: {"node":">=12.0.0"} (current: {"node":"10.24.0","npm":"6.14.5"})
npm WARN notsup Not compatible with your version of node/npm: eslint-plugin-wdio@7.4.2
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.13 (node_modules/watchpack-chokidar2/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.3.2 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})

added 2832 packages from 1097 contributors and audited 2838 packages in 233.485s

243 packages are looking for funding
  run `npm fund` for details

found 170 vulnerabilities (156 moderate, 14 high)
  run `npm audit fix` to fix them, or `npm audit` for details

$ package-lock-lint package-lock.json
0 issues found in: package-lock.json

$ npm update eslint -depth 10

$ package-lock-lint package-lock.json
0 issues found in: package-lock.json

$ ./node_modules/.bin/eslint . --fix

/src/repo/src/components/button/Button.stories.ts
  49:14  warning  There is more than one component in this file  vue/one-component-per-file
  75:14  warning  There is more than one component in this file  vue/one-component-per-file

/src/repo/src/components/button/Button.vue
  11:1  error  ES2015 modules are forbidden  es/no-modules
  12:1  error  ES2015 modules are forbidden  es/no-modules
  13:1  error  ES2015 modules are forbidden  es/no-modules
  20:1  error  ES2015 modules are forbidden  es/no-modules

/src/repo/src/components/checkbox/Checkbox.stories.ts
  31:14  warning  There is more than one component in this file  vue/one-component-per-file
  64:14  warning  There is more than one component in this file  vue/one-component-per-file

/src/repo/src/components/checkbox/Checkbox.vue
   27:1   error    ES2015 modules are forbidden                                      es/no-modules
   28:1   error    ES2015 modules are forbidden                                      es/no-modules
   29:1   error    ES2015 modules are forbidden                                      es/no-modules
   50:1   error    ES2015 modules are forbidden                                      es/no-modules
   80:4   error    Boolean prop should not set a default (Vue defaults it to false)  vue/no-boolean-default
  101:4   error    Boolean prop should not set a default (Vue defaults it to false)  vue/no-boolean-default
  104:27  error    ES2015 destructuring is forbidden                                 es/no-destructuring
  106:3   error    ES2015 block-scoped variables are forbidden                       es/no-block-scoped-variables
  107:3   error    ES2015 block-scoped variables are forbidden                       es/no-block-scoped-variables
  114:3   error    ES2015 block-scoped variables are forbidden                       es/no-block-scoped-variables
  114:22  warning  Missing return type on function                                   @typescript-eslint/explicit-function-return-type
  121:3   error    ES2015 block-scoped variables are forbidden                       es/no-block-scoped-variables
  121:22  warning  Missing return type on function                                   @typescript-eslint/explicit-function-return-type
  128:3   error    ES2015 block-scoped variables are forbidden                       es/no-block-scoped-variables
  129:3   error    ES2015 block-scoped variables are forbidden                       es/no-block-scoped-variables

/src/repo/src/components/icon/Icon.stories.ts
  122:14  warning  There is more than one component in this file  vue/one-component-per-file
  154:14  warning  There is more than one component in this file  vue/one-component-per-file

/src/repo/src/components/icon/Icon.vue
  17:1   error  ES2015 modules are forbidden                 es/no-modules
  18:1   error  ES2015 modules are forbidden                 es/no-modules
  31:1   error  ES2015 modules are forbidden                 es/no-modules
  84:3   error  ES2015 block-scoped variables are forbidden  es/no-block-scoped-variables
  85:27  error  ES2020 optional chaining is forbidden        es/no-optional-chaining

/src/repo/src/components/input/Input.stories.ts
   43:14  warning  There is more than one component in this file  vue/one-component-per-file
   70:14  warning  There is more than one component in this file  vue/one-component-per-file
  168:14  warning  There is more than one component in this file  vue/one-component-per-file

/src/repo/src/components/input/Input.vue
   34:1   error  ES2015 modules are forbidden                                      es/no-modules
   35:1   error  ES2015 modules are forbidden                                      es/no-modules
   36:1   error  ES2015 modules are forbidden                                      es/no-modules
   37:1   error  ES2015 modules are forbidden                                      es/no-modules
   38:1   error  ES2015 modules are forbidden                                      es/no-modules
   40:1   error  ES2015 modules are forbidden                                      es/no-modules
   61:4   error  Boolean prop should not set a default (Vue defaults it to false)  vue/no-boolean-default
   80:4   error  Boolean prop should not set a default (Vue defaults it to false)  vue/no-boolean-default
  114:4   error  ES2015 block-scoped variables are forbidden                       es/no-block-scoped-variables
  115:4   error  ES2015 block-scoped variables are forbidden                       es/no-block-scoped-variables
  115:10  error  ES2015 destructuring is forbidden                                 es/no-destructuring

/src/repo/src/components/radio/Radio.stories.ts
  39:14  warning  There is more than one component in this file  vue/one-component-per-file
  74:14  warning  There is more than one component in this file  vue/one-component-per-file

/src/repo/src/components/radio/Radio.vue
   25:1   error    ES2015 modules are forbidden                                      es/no-modules
   26:1   error    ES2015 modules are forbidden                                      es/no-modules
   27:1   error    ES2015 modules are forbidden                                      es/no-modules
   43:1   error    ES2015 modules are forbidden                                      es/no-modules
   72:4   error    Boolean prop should not set a default (Vue defaults it to false)  vue/no-boolean-default
   84:27  error    ES2015 destructuring is forbidden                                 es/no-destructuring
   86:3   error    ES2015 block-scoped variables are forbidden                       es/no-block-scoped-variables
   93:3   error    ES2015 block-scoped variables are forbidden                       es/no-block-scoped-variables
   93:22  warning  Missing return type on function                                   @typescript-eslint/explicit-function-return-type
  100:3   error    ES2015 block-scoped variables are forbidden                       es/no-block-scoped-variables
  101:3   error    ES2015 block-scoped variables are forbidden                       es/no-block-scoped-variables

/src/repo/src/components/typeahead-search/TypeaheadSearch.vue
  102:1   error    ES2015 modules are forbidden                                      es/no-modules
  103:1   error    ES2015 modules are forbidden                                      es/no-modules
  104:1   error    ES2015 modules are forbidden                                      es/no-modules
  105:1   error    ES2015 modules are forbidden                                      es/no-modules
  106:1   error    ES2015 modules are forbidden                                      es/no-modules
  107:1   error    ES2015 modules are forbidden                                      es/no-modules
  108:1   error    ES2015 modules are forbidden                                      es/no-modules
  113:1   error    ES2015 modules are forbidden                                      es/no-modules
  114:1   error    ES2015 modules are forbidden                                      es/no-modules
  115:1   error    ES2015 modules are forbidden                                      es/no-modules
  116:1   error    ES2015 modules are forbidden                                      es/no-modules
  117:1   error    ES2015 modules are forbidden                                      es/no-modules
  119:1   error    ES2015 modules are forbidden                                      es/no-modules
  121:1   warning  This line has a length of 135. Maximum allowed is 100             max-len
  161:4   error    Boolean prop should not set a default (Vue defaults it to false)  vue/no-boolean-default
  169:4   error    Boolean prop should not set a default (Vue defaults it to false)  vue/no-boolean-default
  173:4   error    Boolean prop should not set a default (Vue defaults it to false)  vue/no-boolean-default
  177:4   error    Boolean prop should not set a default (Vue defaults it to false)  vue/no-boolean-default
  276:5   error    ES2015 block-scoped variables are forbidden                       es/no-block-scoped-variables
  295:41  error    ES2015 destructuring is forbidden                                 es/no-destructuring
  299:6   error    ES2015 block-scoped variables are forbidden                       es/no-block-scoped-variables
  355:4   error    ES2015 block-scoped variables are forbidden                       es/no-block-scoped-variables
  386:4   error    ES2015 block-scoped variables are forbidden                       es/no-block-scoped-variables
  406:4   error    ES2015 block-scoped variables are forbidden                       es/no-block-scoped-variables
  449:4   error    ES2015 block-scoped variables are forbidden                       es/no-block-scoped-variables

/src/repo/src/components/typeahead-suggestion-title/TypeaheadSuggestionTitle.vue
   9:1  error  ES2015 modules are forbidden  es/no-modules
  10:1  error  ES2015 modules are forbidden  es/no-modules
  12:1  error  ES2015 modules are forbidden  es/no-modules

/src/repo/src/components/typeahead-suggestion/TypeaheadSuggestion.stories.ts
   35:14  warning  There is more than one component in this file  vue/one-component-per-file
  116:15  warning  There is more than one component in this file  vue/one-component-per-file

/src/repo/src/components/typeahead-suggestion/TypeaheadSuggestion.vue
   37:1   error  ES2015 modules are forbidden                                      es/no-modules
   38:1   error  ES2015 modules are forbidden                                      es/no-modules
   39:1   error  ES2015 modules are forbidden                                      es/no-modules
   40:1   error  ES2015 modules are forbidden                                      es/no-modules
   41:1   error  ES2015 modules are forbidden                                      es/no-modules
   43:1   error  ES2015 modules are forbidden                                      es/no-modules
   45:1   error  ES2015 modules are forbidden                                      es/no-modules
   51:4   error  Boolean prop should not set a default (Vue defaults it to false)  vue/no-boolean-default
   71:4   error  Boolean prop should not set a default (Vue defaults it to false)  vue/no-boolean-default
   75:4   error  Boolean prop should not set a default (Vue defaults it to false)  vue/no-boolean-default
   79:4   error  Boolean prop should not set a default (Vue defaults it to false)  vue/no-boolean-default
  107:45  error  ES2020 optional chaining is forbidden                             es/no-optional-chaining

✖ 99 problems (82 errors, 17 warnings)


$ ./node_modules/.bin/eslint . -f json
[{"filePath":"/src/repo/.jest/ignoreTransform.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/.jest/snapshotResolver.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/.storybook/main.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/.storybook/preview.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/fork-ts-checker.config.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/src/components/button/Button.stories.ts","messages":[{"ruleId":"vue/one-component-per-file","severity":1,"message":"There is more than one component in this file.","line":49,"column":14,"nodeType":"ObjectExpression","messageId":"toManyComponents","endLine":72,"endColumn":3},{"ruleId":"vue/one-component-per-file","severity":1,"message":"There is more than one component in this file.","line":75,"column":14,"nodeType":"ObjectExpression","messageId":"toManyComponents","endLine":133,"endColumn":3}],"errorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import Vue from 'vue';\nimport { Args, StoryContext } from '@storybook/addons';\nimport WvuiButton from './Button.vue';\nimport WvuiIcon from '../icon/Icon.vue';\nimport { ButtonType } from './ButtonType';\nimport { ButtonAction } from './ButtonAction';\nimport { filterKeys, makeActionArgTypes, makeActionListeners } from '../../utils/StoryUtils';\nimport './Button.stories.less';\nimport { lookupIcon, makeOptionalIconArgType } from '../icon/Icon.stories';\n\nexport default {\n\ttitle: 'Components/Button',\n\tcomponent: WvuiButton,\n\targTypes: {\n\t\taction: {\n\t\t\t// eslint-disable-next-line es/no-object-values\n\t\t\toptions: Object.values( ButtonAction ),\n\t\t\tcontrol: 'inline-radio',\n\t\t\tdefaultValue: ButtonAction.Default\n\t\t},\n\t\ttype: {\n\t\t\t// eslint-disable-next-line es/no-object-values\n\t\t\toptions: Object.values( ButtonType ),\n\t\t\tcontrol: 'inline-radio',\n\t\t\tdefaultValue: ButtonType.Normal\n\t\t},\n\t\tdefault: {\n\t\t\tcontrol: 'text',\n\t\t\tdefaultValue: 'Click me'\n\t\t},\n\t\tdisabled: {\n\t\t\tcontrol: 'boolean',\n\t\t\ttable: {\n\t\t\t\tcategory: 'Attributes'\n\t\t\t}\n\t\t},\n\t\ticon: {\n\t\t\t...makeOptionalIconArgType(),\n\t\t\tdescription: 'Icon to prepend to the button content'\n\t\t},\n\t\t...makeActionArgTypes( [ 'click' ] )\n\t},\n\tparameters: {\n\t\tlayout: 'centered'\n\t}\n};\n\nexport const Configurable = ( args : Args, { argTypes } : StoryContext ): Vue.Component =>\n\tVue.extend( {\n\t\tcomponents: { WvuiButton, WvuiIcon },\n\t\tprops: Object.keys( argTypes ),\n\t\tcomputed: {\n\t\t\tslotContents() {\n\t\t\t\treturn this.default;\n\t\t\t},\n\t\t\ticonData() {\n\t\t\t\treturn lookupIcon( this.icon );\n\t\t\t},\n\t\t\tactionListeners() {\n\t\t\t\treturn makeActionListeners( args, argTypes );\n\t\t\t},\n\t\t\tfilteredProps() {\n\t\t\t\treturn filterKeys( this.$props, [ 'default', 'icon' ] );\n\t\t\t}\n\t\t},\n\t\ttemplate: `\n\t\t\t<wvui-button v-bind=\"filteredProps\" v-on=\"actionListeners\">\n\t\t\t\t<wvui-icon v-if=\"iconData\" :icon=\"iconData\" />\n\t\t\t\t{{ slotContents }}\n\t\t\t</wvui-button>\n\t\t`\n\t} );\n\nexport const AllCombinations = ( _args: Args, { argTypes } : StoryContext ): Vue.Component =>\n\tVue.extend( {\n\t\tcomponents: { WvuiButton, WvuiIcon },\n\t\tprops: Object.keys( argTypes ),\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\ttypes: ButtonType,\n\t\t\t\tactions: ButtonAction\n\t\t\t};\n\t\t},\n\t\tcomputed: {\n\t\t\tslotContents() {\n\t\t\t\treturn this.default;\n\t\t\t},\n\t\t\ticonData() {\n\t\t\t\treturn lookupIcon( this.icon );\n\t\t\t}\n\t\t},\n\t\ttemplate: `\n\t\t\t<table class=\"sb-button-combinations\">\n\t\t\t\t<thead>\n\t\t\t\t\t<th></th>\n\t\t\t\t\t<th v-for=\"(action, actionName) in actions\" :key=\"action\" scope=\"col\">\n\t\t\t\t\t\t{{ actionName }}\n\t\t\t\t\t</th>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t<template v-for=\"(type, typeName) in types\">\n\t\t\t\t\t\t<tr v-for=\"disabled in [ false, true ]\" :key=\"type + disabled\">\n\t\t\t\t\t\t\t<th scope=\"row\">\n\t\t\t\t\t\t\t\t{{ typeName }} {{ disabled ? 'disabled' : '' }}\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<td v-for=\"(action, actionName) in actions\" :key=\"action\">\n\t\t\t\t\t\t\t\t<wvui-button\n\t\t\t\t\t\t\t\t\t:action=\"action\"\n\t\t\t\t\t\t\t\t\t:type=\"type\"\n\t\t\t\t\t\t\t\t\t:disabled=\"disabled\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<wvui-icon\n\t\t\t\t\t\t\t\t\t\tv-if=\"iconData\"\n\t\t\t\t\t\t\t\t\t\t:icon=\"iconData\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t{{ slotContents }}\n\t\t\t\t\t\t\t\t</wvui-button>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</template>\n\t\t\t\t</tbody>\n\t\t\t\t<tfoot class=\"sb-button-combinations-hint-mobile\">\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td\n\t\t\t\t\t\t\t:colspan=\"Object.keys( actions ).length + 1\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tPlease scroll horizontally to see all combinations.\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t</tfoot>\n\t\t\t</table>\n\t\t`\n\t} );\n\nAllCombinations.argTypes = {\n\taction: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t},\n\tdisabled: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t},\n\ttype: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t}\n};\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/button/Button.test.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"jest/no-try-expect","replacedBy":["no-conditional-expect"]},{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/button/Button.vue","messages":[{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":11,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":11,"endColumn":57},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":12,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":12,"endColumn":63},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":13,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":13,"endColumn":37},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":20,"column":1,"nodeType":"ExportDefaultDeclaration","messageId":"forbidden","endLine":61,"endColumn":5}],"errorCount":4,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<button class=\"wvui-button\" :class=\"rootClasses\" @click=\"onClick\">\n\t\t<!--\n\t\t\t@slot Button content\n\t\t-->\n\t\t<slot />\n\t</button>\n</template>\n\n<script lang=\"ts\">\nimport { ButtonType, isButtonType } from './ButtonType';\nimport { ButtonAction, isButtonAction } from './ButtonAction';\nimport Vue, { PropType } from 'vue';\n\n/**\n * A button wrapping slotted content.\n *\n * @fires {Event} click\n */\nexport default Vue.extend( {\n\tname: 'WvuiButton',\n\tprops: {\n\t\t/**\n\t\t * What type of action the button will cause to be taken when clicked.\n\t\t * See ButtonAction for what each value means.\n\t\t */\n\t\taction: {\n\t\t\ttype: String as PropType<ButtonAction>,\n\t\t\tdefault: ButtonAction.Default,\n\t\t\t// use arrow function for type inference of property\n\t\t\tvalidator: function ( value ) { return isButtonAction( value ); }\n\t\t},\n\t\t/**\n\t\t * Button type. See ButtonType for what each value means.\n\t\t */\n\t\ttype: {\n\t\t\ttype: String as PropType<ButtonType>,\n\t\t\tdefault: ButtonType.Normal,\n\t\t\t// use arrow function for type inference of property\n\t\t\tvalidator: function ( value ) { return isButtonType( value ); }\n\t\t}\n\t},\n\tcomputed: {\n\t\trootClasses: function (): Record<string, boolean> {\n\t\t\treturn {\n\t\t\t\t'wvui-button--action-default': this.action === ButtonAction.Default,\n\t\t\t\t'wvui-button--action-progressive': this.action === ButtonAction.Progressive,\n\t\t\t\t'wvui-button--action-destructive': this.action === ButtonAction.Destructive,\n\t\t\t\t'wvui-button--type-primary': this.type === ButtonType.Primary,\n\t\t\t\t'wvui-button--type-normal': this.type === ButtonType.Normal,\n\t\t\t\t'wvui-button--type-quiet': this.type === ButtonType.Quiet,\n\t\t\t\t'wvui-button--framed': this.type !== ButtonType.Quiet\n\t\t\t};\n\t\t}\n\t},\n\tmethods: {\n\t\tonClick: function ( event: Event ): void {\n\t\t\tthis.$emit( 'click', event );\n\t\t}\n\t}\n} );\n</script>\n\n<style lang=\"less\">\n@import ( reference ) '@/themes/wikimedia-ui.less';\n\n.wvui-button {\n\tbox-sizing: border-box;\n\t// Interactive elements have a minimum touch area.\n\tmin-width: @min-size-base;\n\tmin-height: @min-size-base;\n\tmax-width: @max-width-button;\n\t// Support Firefox, Safari: Normalize by removing the `margin`.\n\tmargin: 0;\n\tborder-width: @border-width-base;\n\tborder-style: @border-style-base;\n\tborder-radius: @border-radius-base;\n\tpadding-right: @padding-horizontal-base;\n\tpadding-left: @padding-horizontal-base;\n\t// Support IE 11: Normalize by showing `overflow`.\n\toverflow: visible;\n\t// Support all browsers: Normalize by inheriting `font-family`.\n\t// Initial value depends on user-agent.\n\tfont-family: inherit;\n\t// Support all browsers: Normalize by inheriting `font-size` over initial value of `none`.\n\tfont-size: inherit;\n\tfont-weight: bold;\n\t// Support Edge, Firefox, and IE: Normalize by removing the inheritance of `text-transform`.\n\ttext-transform: none;\n\t// Contents are single line.\n\twhite-space: nowrap;\n\ttransition: border-color @transition-base, background-color @transition-base, color @transition-base, box-shadow @transition-base;\n\n\t// Support Firefox: Normalize by hiding the inner focus `border` and `padding`.\n\t&::-moz-focus-inner {\n\t\tborder: 0;\n\t\tpadding: 0;\n\t}\n\n\t&:focus {\n\t\t// Hide the standard focus outline. A border and box-shadow representation is added below.\n\t\toutline: 0;\n\t}\n\n\t&:not( [ disabled ] ) {\n\t\tcolor: @color-base;\n\t\t// Use hand cursor. This is nonstandard for a button but allows for a visible\n\t\t// interactivity distinction from the disabled state.\n\t\tcursor: pointer;\n\n\t\t&:focus {\n\t\t\tborder-color: @color-primary--focus;\n\t\t\tbox-shadow: @box-shadow-base--focus;\n\t\t\t// In Windows high contrast mode the outline becomes visible.\n\t\t\toutline: @outline-base--focus;\n\t\t}\n\n\t\t&:active {\n\t\t\tbackground-color: @background-color-framed--active;\n\t\t\tcolor: @color-base--emphasized;\n\t\t\tbox-shadow: none;\n\t\t}\n\t}\n\n\t&[ disabled ] {\n\t\tborder-color: transparent;\n\t}\n\n\t.wvui-icon {\n\t\t// Any icons used in the button content should have the color of the surrounding text\n\t\t// This overrides the color rule in Icon.vue, and ensures that the rules below changing the\n\t\t// text color for progressive and destructive buttons also apply to icons.\n\t\tcolor: inherit;\n\t}\n}\n\n// Non-quiet “framed” buttons (normal and primary types)\n.wvui-button--framed {\n\t&:not( [ disabled ] ) {\n\t\tbackground-color: @background-color-framed;\n\t\tborder-color: @border-color-base;\n\n\t\t&:hover {\n\t\t\tbackground-color: @background-color-framed--hover;\n\t\t\tcolor: @color-base--hover;\n\t\t}\n\n\t\t&:active {\n\t\t\tbackground-color: @background-color-framed--active;\n\t\t\tcolor: @color-base--active;\n\t\t\tborder-color: @border-color-base--active;\n\t\t}\n\t}\n\n\t&[ disabled ] {\n\t\tbackground-color: @background-color-filled--disabled;\n\t\tcolor: @color-filled--disabled;\n\t}\n}\n\n.wvui-button--type-primary {\n\t// Progressive primary buttons\n\t&.wvui-button--action-progressive:not( [ disabled ] ) {\n\t\tbackground-color: @color-primary;\n\t\tcolor: @color-base--inverted;\n\t\tborder-color: @color-primary;\n\n\t\t&:hover {\n\t\t\tbackground-color: @color-primary--hover;\n\t\t\tborder-color: @color-primary--hover;\n\t\t}\n\n\t\t&:focus {\n\t\t\tbackground-color: @color-primary--focus;\n\t\t\tborder-color: @color-primary--focus;\n\t\t\tbox-shadow: @box-shadow-primary--focus;\n\t\t}\n\n\t\t&:active {\n\t\t\tbackground-color: @color-primary--active;\n\t\t\tborder-color: @color-primary--active;\n\t\t\t// Reset `:focus` box shadow to amplify 'interaction' feeling when pressed.\n\t\t\tbox-shadow: none;\n\t\t}\n\t}\n\n\t// Destructive primary buttons\n\t&.wvui-button--action-destructive:not( [ disabled ] ) {\n\t\tbackground-color: @color-destructive;\n\t\tcolor: @color-base--inverted;\n\t\tborder-color: @color-destructive;\n\n\t\t&:hover {\n\t\t\tbackground-color: @color-destructive--hover;\n\t\t\tborder-color: @color-destructive--hover;\n\t\t}\n\n\t\t&:focus {\n\t\t\tbackground-color: @color-destructive--focus;\n\t\t\tborder-color: @color-destructive--focus;\n\t\t\tbox-shadow: @box-shadow-destructive--focus;\n\t\t}\n\n\t\t&:active {\n\t\t\tbackground-color: @color-destructive--active;\n\t\t\tborder-color: @color-destructive--active;\n\t\t\t// Reset `:focus` box shadow to amplify 'interaction' feeling when pressed.\n\t\t\tbox-shadow: none;\n\t\t}\n\t}\n}\n\n.wvui-button--type-normal {\n\t// Normal progressive buttons\n\t&.wvui-button--action-progressive:not( [ disabled ] ) {\n\t\tcolor: @color-primary;\n\n\t\t&:hover {\n\t\t\tcolor: @color-primary--hover;\n\t\t\tborder-color: @border-color-primary--hover;\n\t\t}\n\n\t\t&:focus {\n\t\t\tcolor: @color-primary--focus;\n\t\t\tborder-color: @border-color-primary--focus;\n\t\t\tbox-shadow: @box-shadow-primary--focus;\n\t\t}\n\n\t\t&:active {\n\t\t\tbackground-color: lighten( @color-primary--active, 60% );\n\t\t\tcolor: @color-primary--active;\n\t\t\tborder-color: @border-color-primary--active;\n\t\t\t// Reset `:focus` box shadow to amplify 'interaction' feeling when pressed.\n\t\t\tbox-shadow: none;\n\t\t}\n\t}\n\n\t// Normal destructive buttons\n\t&.wvui-button--action-destructive:not( [ disabled ] ) {\n\t\tcolor: @color-destructive;\n\n\t\t&:hover {\n\t\t\tcolor: @color-destructive--hover;\n\t\t\tborder-color: @border-color-destructive--hover;\n\t\t}\n\n\t\t&:focus {\n\t\t\tcolor: @color-destructive--focus;\n\t\t\tborder-color: @border-color-destructive--focus;\n\t\t\tbox-shadow: @box-shadow-destructive--focus;\n\t\t}\n\n\t\t&:active {\n\t\t\tbackground-color: lighten( @color-destructive--active, 60% );\n\t\t\tcolor: @color-destructive--active;\n\t\t\tborder-color: @border-color-destructive--active;\n\t\t\t// Reset `:focus` box shadow to amplify 'interaction' feeling when pressed.\n\t\t\tbox-shadow: none;\n\t\t}\n\t}\n}\n\n// Quiet buttons.\n.wvui-button--type-quiet {\n\tbackground-color: transparent;\n\tcolor: @color-base;\n\tborder-color: transparent;\n\n\t&:not( [ disabled ] ) {\n\t\t&:hover {\n\t\t\tbackground-color: @background-color-quiet--hover;\n\t\t}\n\n\t\t&:focus {\n\t\t\tborder-color: @border-color-primary--focus;\n\t\t\tbox-shadow: @box-shadow-primary--focus;\n\t\t}\n\n\t\t&:active {\n\t\t\tbackground-color: @background-color-quiet--active;\n\t\t\tborder-color: @border-color-base--active;\n\t\t\tbox-shadow: none;\n\t\t}\n\t}\n\n\t// Progressive quiet buttons.\n\t&.wvui-button--action-progressive:not( [ disabled ] ) {\n\t\tcolor: @color-primary;\n\n\t\t&:hover {\n\t\t\t// FIXME @background-color-primary--hover exists but is a little lighter\n\t\t\tbackground-color: fade( #347bff, 20% );\n\t\t\tcolor: @color-primary--hover;\n\t\t}\n\n\t\t&:focus {\n\t\t\tcolor: @color-primary--focus;\n\t\t\tborder-color: @border-color-primary--focus;\n\t\t\tbox-shadow: @box-shadow-primary--focus;\n\t\t}\n\n\t\t&:active {\n\t\t\tbackground-color: @color-primary--active;\n\t\t\tcolor: @color-base--inverted;\n\t\t\tborder-color: @color-primary--active;\n\t\t\tbox-shadow: none;\n\t\t}\n\t}\n\n\t// Destructive quiet buttons.\n\t&.wvui-button--action-destructive:not( [ disabled ] ) {\n\t\tcolor: @color-destructive;\n\n\t\t&:hover {\n\t\t\t// FIXME @background-color-destructive--hover should exist but doesn't\n\t\t\tbackground-color: fade( #d11d13, 20% );\n\t\t\tcolor: @color-destructive--hover;\n\t\t}\n\n\t\t&:focus {\n\t\t\tcolor: @color-destructive--focus;\n\t\t\tborder-color: @border-color-destructive--focus;\n\t\t\tbox-shadow: @box-shadow-destructive--focus;\n\t\t}\n\n\t\t&:active {\n\t\t\tbackground-color: @color-destructive--active;\n\t\t\tcolor: @color-base--inverted;\n\t\t\tborder-color: @color-destructive--active;\n\t\t\tbox-shadow: none;\n\t\t}\n\t}\n\n\t&[ disabled ] {\n\t\tcolor: @color-base--disabled;\n\t}\n}\n</style>\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/button/ButtonAction.test.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"jest/no-try-expect","replacedBy":["no-conditional-expect"]},{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/button/ButtonAction.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/button/ButtonType.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/checkbox/Checkbox.stories.ts","messages":[{"ruleId":"vue/one-component-per-file","severity":1,"message":"There is more than one component in this file.","line":31,"column":14,"nodeType":"ObjectExpression","messageId":"toManyComponents","endLine":61,"endColumn":3},{"ruleId":"vue/one-component-per-file","severity":1,"message":"There is more than one component in this file.","line":64,"column":14,"nodeType":"ObjectExpression","messageId":"toManyComponents","endLine":129,"endColumn":3}],"errorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import Vue from 'vue';\nimport { Args, StoryContext } from '@storybook/addons';\nimport WvuiCheckbox from './Checkbox.vue';\nimport { filterKeys, makeActionArgTypes, makeActionListeners } from '../../utils/StoryUtils';\nimport './Checkbox.stories.less';\n\nexport default {\n\ttitle: 'Components/Checkbox',\n\tcomponent: WvuiCheckbox,\n\tparameters: {\n\t\tlayout: 'centered'\n\t},\n\targTypes: {\n\t\tdefault: {\n\t\t\tcontrol: 'text',\n\t\t\tdefaultValue: 'Configurable checkbox input'\n\t\t},\n\t\t'v-model': {\n\t\t\tname: 'modelValue',\n\t\t\tcontrol: null\n\t\t},\n\t\tinputValue: {\n\t\t\tcontrol: 'text',\n\t\t\tdefaultValue: 'checkbox-configurable'\n\t\t},\n\t\t...makeActionArgTypes( [ 'input' ] )\n\t}\n};\n\nexport const Configurable = ( args : Args, { argTypes } : StoryContext ): Vue.Component =>\n\tVue.extend( {\n\t\tcomponents: { WvuiCheckbox },\n\t\tprops: Object.keys( argTypes ),\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tcurrentValue: false\n\t\t\t};\n\t\t},\n\t\tcomputed: {\n\t\t\tactionListeners() {\n\t\t\t\treturn makeActionListeners( args, argTypes );\n\t\t\t},\n\t\t\tfilteredProps() {\n\t\t\t\treturn filterKeys( this.$props, [ 'default', 'vModel' ] );\n\t\t\t},\n\t\t\tslotContents() {\n\t\t\t\treturn this.default;\n\t\t\t}\n\t\t},\n\t\ttemplate: `\n\t\t\t<div class=\"sb-checkbox-wrapper\">\n\t\t\t\t<wvui-checkbox\n\t\t\t\t\tv-model=\"currentValue\"\n\t\t\t\t\tv-bind=\"filteredProps\"\n\t\t\t\t\tv-on=\"actionListeners\"\n\t\t\t\t>\n\t\t\t\t\t{{ slotContents }}\n\t\t\t\t</wvui-checkbox>\n\t\t\t</div>\n\t\t`\n\t} );\n\nexport const CheckboxGroup = ( args : Args, { argTypes } : StoryContext ): Vue.Component =>\n\tVue.extend( {\n\t\tcomponents: { WvuiCheckbox },\n\t\tprops: Object.keys( argTypes ),\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tcheckboxesValue: [\n\t\t\t\t\t'checkbox-1',\n\t\t\t\t\t'checkbox-2',\n\t\t\t\t\t'checkbox-6'\n\t\t\t\t],\n\t\t\t\tcheckboxes: [\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: 'Checkbox 1',\n\t\t\t\t\t\tvalue: 'checkbox-1',\n\t\t\t\t\t\tdisabled: false\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: 'Checkbox 2 (initially selected)',\n\t\t\t\t\t\tvalue: 'checkbox-2',\n\t\t\t\t\t\tdisabled: false\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: 'Checkbox 3, with a very long label that spans onto a second line',\n\t\t\t\t\t\tvalue: 'checkbox-3',\n\t\t\t\t\t\tdisabled: false\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: 'Checkbox 4 (indeterminate)',\n\t\t\t\t\t\tvalue: 'checkbox-4',\n\t\t\t\t\t\tindeterminate: true,\n\t\t\t\t\t\tdisabled: false\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: 'Checkbox 5 (disabled)',\n\t\t\t\t\t\tvalue: 'checkbox-5',\n\t\t\t\t\t\tdisabled: true\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: 'Checkbox 6 (initially selected, disabled)',\n\t\t\t\t\t\tvalue: 'checkbox-6',\n\t\t\t\t\t\tdisabled: true\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t};\n\t\t},\n\t\tcomputed: {\n\t\t\tactionListeners() {\n\t\t\t\treturn makeActionListeners( args, argTypes );\n\t\t\t}\n\t\t},\n\t\ttemplate: `\n\t\t\t<div class=\"sb-checkbox-wrapper\">\n\t\t\t\t<wvui-checkbox\n\t\t\t\t\tv-for=\"checkbox in checkboxes\"\n\t\t\t\t\t:key=\"checkbox.value\"\n\t\t\t\t\tv-model=\"checkboxesValue\"\n\t\t\t\t\t:input-value=\"checkbox.value\"\n\t\t\t\t\t:disabled=\"checkbox.disabled\"\n\t\t\t\t\t:indeterminate=\"checkbox.indeterminate\"\n\t\t\t\t\tv-on=\"actionListeners\"\n\t\t\t\t>\n\t\t\t\t\t{{ checkbox.label }}\n\t\t\t\t</wvui-checkbox>\n\t\t\t</div>\n\t\t`\n\t} );\n\nCheckboxGroup.argTypes = {\n\tdefault: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t},\n\tinputValue: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t},\n\tdisabled: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t},\n\tindeterminate: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t}\n};\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/checkbox/Checkbox.test.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"jest/no-try-expect","replacedBy":["no-conditional-expect"]},{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/checkbox/Checkbox.vue","messages":[{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":27,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":27,"endColumn":23},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":28,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":28,"endColumn":87},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":29,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":29,"endColumn":85},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":50,"column":1,"nodeType":"ExportDefaultDeclaration","messageId":"forbidden","endLine":139,"endColumn":5},{"ruleId":"vue/no-boolean-default","severity":2,"message":"Boolean prop should not set a default (Vue defaults it to false).","line":80,"column":4,"nodeType":"Property","endLine":80,"endColumn":18},{"ruleId":"vue/no-boolean-default","severity":2,"message":"Boolean prop should not set a default (Vue defaults it to false).","line":101,"column":4,"nodeType":"Property","endLine":101,"endColumn":18},{"ruleId":"es/no-destructuring","severity":2,"message":"ES2015 destructuring is forbidden.","line":104,"column":27,"nodeType":"ObjectPattern","messageId":"forbidden","endLine":104,"endColumn":35},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":106,"column":3,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":106,"endColumn":53},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":107,"column":3,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":107,"endColumn":53},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":114,"column":3,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":116,"endColumn":5},{"ruleId":"@typescript-eslint/explicit-function-return-type","severity":1,"message":"Missing return type on function.","line":114,"column":22,"nodeType":"FunctionExpression","messageId":"missingReturnType","endLine":114,"endColumn":33},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":121,"column":3,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":123,"endColumn":5},{"ruleId":"@typescript-eslint/explicit-function-return-type","severity":1,"message":"Missing return type on function.","line":121,"column":22,"nodeType":"FunctionExpression","messageId":"missingReturnType","endLine":121,"endColumn":33},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":128,"column":3,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":128,"endColumn":54},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":129,"column":3,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":129,"endColumn":63}],"errorCount":13,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<label\n\t\tref=\"label\"\n\t\tclass=\"wvui-checkbox\"\n\t\t:aria-disabled=\"disabled\"\n\t\t@click=\"focusInput\"\n\t\t@keydown.prevent.enter=\"clickLabel\"\n\t>\n\t\t<input\n\t\t\tref=\"input\"\n\t\t\tv-model=\"wrappedModel\"\n\t\t\tclass=\"wvui-checkbox__input\"\n\t\t\ttype=\"checkbox\"\n\t\t\t:value=\"inputValue\"\n\t\t\t:disabled=\"disabled\"\n\t\t\t:indeterminate.prop=\"indeterminate\"\n\t\t>\n\t\t<span class=\"wvui-checkbox__icon\" />\n\t\t<span class=\"wvui-checkbox__label-content\">\n\t\t\t<!-- @slot Input label content -->\n\t\t\t<slot />\n\t\t</span>\n\t</label>\n</template>\n\n<script lang=\"ts\">\nimport Vue from 'vue';\nimport VueCompositionAPI, { defineComponent, ref, toRef } from '@vue/composition-api';\nimport useModelWrapper, { modelValueProp } from '../../composables/useModelWrapper';\n\nVue.use( VueCompositionAPI );\n\n/**\n * A binary input that can exist by itself or in a group. When in a group, any\n * number of checkboxes can be checked at a time.\n *\n * Typical use will involve using v-for to loop through an array of items and\n * output a Checkbox component for each one. Each Checkbox will have the same\n * v-model prop, but different inputValue props and label content.\n *\n * For a single checkbox, the v-model value will be a boolean true when the box\n * is checked and false when unchecked.\n *\n * For multiple checkboxes, the v-model value will be an array of the\n * inputValues of any current checked boxes (or an empty array if no boxes are\n * checked).\n *\n * @fires {Event} input\n */\nexport default defineComponent( {\n\tname: 'WvuiCheckbox',\n\tmodel: {\n\t\tprop: 'modelValue',\n\t\tevent: 'input'\n\t},\n\tprops: {\n\t\t/**\n\t\t * Value provided by v-model in a parent component.\n\t\t *\n\t\t * Rather than directly binding a value prop to this component, use\n\t\t * v-model on this component in the parent component.\n\t\t */\n\t\tmodelValue: modelValueProp,\n\t\t/**\n\t\t * HTML \"value\" attribute to assign to the input.\n\t\t *\n\t\t * A unique inputValue is required when using the same v-model for\n\t\t * multiple inputs. If this is a standalone checkbox, the inputValue\n\t\t * prop can be ommitted and will default to false.\n\t\t */\n\t\tinputValue: {\n\t\t\ttype: [ String, Number, Boolean ],\n\t\t\tdefault: false\n\t\t},\n\t\t/**\n\t\t * Whether the disabled attribute should be added to the input.\n\t\t */\n\t\tdisabled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false\n\t\t},\n\t\t/**\n\t\t * Whether the indeterminate visual state should be displayed.\n\t\t *\n\t\t * The indeterminate state indicates that a checkbox is neither on nor\n\t\t * off. Within this component, this state is purely visual. The parent\n\t\t * component must house the logic to set a checkbox to the indeterminate\n\t\t * state via this prop (e.g. in the case of a set of nested checkboxes\n\t\t * where some boxes are checked and some are not, making the parent\n\t\t * checkbox neither fully on nor fully off).\n\t\t *\n\t\t * This prop is independent of the value provided by v-model. If\n\t\t * indeterminate is set to true, the indeterminate visual state will\n\t\t * display, but the value will not be affected. Nor will the value\n\t\t * affect the visual state: indeterminate overrides the checked and\n\t\t * unchecked visual states. If indeterminate changes to false, the\n\t\t * visual state will reflect the current v-model value.\n\t\t */\n\t\tindeterminate: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false\n\t\t}\n\t},\n\tsetup: function ( props, { emit } ) {\n\t\t// Declare template refs.\n\t\tconst input = ref<HTMLInputElement | undefined>();\n\t\tconst label = ref<HTMLLabelElement | undefined>();\n\n\t\t/**\n\t\t * When the label is clicked, focus on the input.\n\t\t *\n\t\t * This doesn't happen automatically in Firefox or Safari.\n\t\t */\n\t\tconst focusInput = function () {\n\t\t\t( input.value as HTMLInputElement ).focus();\n\t\t};\n\n\t\t/**\n\t\t * On enter keydown, click the label to toggle the input.\n\t\t */\n\t\tconst clickLabel = function () {\n\t\t\t( label.value as HTMLLabelElement ).click();\n\t\t};\n\n\t\t// Take the modelValue provided by the parent component via v-model and\n\t\t// generate a wrapped model that we can use for the input element in\n\t\t// this component.\n\t\tconst modelValueRef = toRef( props, 'modelValue' );\n\t\tconst wrappedModel = useModelWrapper( modelValueRef, emit );\n\n\t\treturn {\n\t\t\tinput: input,\n\t\t\tlabel: label,\n\t\t\twrappedModel: wrappedModel,\n\t\t\tfocusInput: focusInput,\n\t\t\tclickLabel: clickLabel\n\t\t};\n\t}\n} );\n</script>\n\n<style lang=\"less\">\n@import ( reference ) '@/themes/wikimedia-ui.less';\n@import ( reference ) '@/themes/mixins/binary-input.less';\n\n.wvui-checkbox {\n\t.wvui-mixin-binary-input();\n\n\t// Custom-styled checkbox that's visible to the user.\n\t&__icon {\n\t\t// stylelint-disable-next-line plugin/no-unsupported-browser-features\n\t\tbackground-size: 0 0;\n\t\tborder-radius: @border-radius-base;\n\t\ttransition: background-color @transition-base, border-color @transition-base, box-shadow @transition-base;\n\t}\n\n\t// HTML `<input type=\"checkbox\">`.\n\t// Based on the HTML attributes of the checkbox input, we can change the\n\t// style of the adjacent span, which will look like a custom-styled\n\t// checkbox.\n\t&__input {\n\t\t&:focus + .wvui-checkbox__icon {\n\t\t\tborder-color: @border-color-input-binary--focus;\n\t\t\tbox-shadow: @box-shadow-input-binary--focus;\n\t\t\t// In Windows high contrast mode the outline becomes visible.\n\t\t\toutline: 1px solid transparent;\n\t\t}\n\n\t\t&:hover + .wvui-checkbox__icon {\n\t\t\tborder-color: @border-color-input-binary--hover;\n\t\t}\n\n\t\t&:indeterminate + .wvui-checkbox__icon {\n\t\t\t&:before {\n\t\t\t\tcontent: ' ';\n\t\t\t\tbackground-color: @color-base--inverted;\n\t\t\t\tposition: absolute;\n\t\t\t\ttop: 50%;\n\t\t\t\tright: @start-input-binary-icon;\n\t\t\t\tleft: @start-input-binary-icon;\n\t\t\t\theight: @border-width-base * 2;\n\t\t\t\tmargin-top: -@border-width-base;\n\t\t\t}\n\t\t}\n\n\t\t// Styles for the checked checkbox that apply whether or not the input\n\t\t// is enabled or disabled.\n\t\t&:checked + .wvui-checkbox__icon {\n\t\t\t// TODO: temporary workaround for check icon SVG. This should be\n\t\t\t// updated once we have a more permanent location and format for\n\t\t\t// icon SVGs (see https://phabricator.wikimedia.org/T282625).\n\t\t\t// stylelint-disable-next-line function-url-quotes\n\t\t\tbackground-image: url( 'data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\"><title>check</title><g fill=\"%23fff\"><path d=\"M7 14.17L2.83 10l-1.41 1.41L7 17 19 5l-1.41-1.42z\"/></g></svg>' );\n\t\t\tbackground-position: center;\n\t\t\tbackground-repeat: no-repeat;\n\t\t\t// This must have two values to match `background-size: 0 0` above,\n\t\t\t// otherwise the transition does not work (at least in Chrome).\n\t\t\t// stylelint-disable-next-line plugin/no-unsupported-browser-features\n\t\t\tbackground-size: @size-icon-small @size-icon-small;\n\t\t}\n\n\t\t&:disabled {\n\t\t\t& + .wvui-checkbox__icon {\n\t\t\t\tbackground-color: @background-color-filled--disabled;\n\t\t\t\tborder-color: @border-color-base--disabled;\n\t\t\t\tbox-shadow: none;\n\t\t\t}\n\n\t\t\t& ~ .wvui-checkbox__label-content {\n\t\t\t\tcolor: @color-base--disabled;\n\t\t\t}\n\t\t}\n\n\t\t&:checked:enabled,\n\t\t&:indeterminate:enabled {\n\t\t\t& + .wvui-checkbox__icon {\n\t\t\t\tbackground-color: @background-color-input-binary--checked;\n\t\t\t\tborder-color: @border-color-input-binary--checked;\n\t\t\t}\n\n\t\t\t&:focus + .wvui-checkbox__icon {\n\t\t\t\tbackground-color: @background-color-input-binary--checked;\n\t\t\t\tborder-color: @border-color-input-binary--checked;\n\t\t\t\tbox-shadow: @box-shadow-input-checkbox--focus-checked;\n\t\t\t}\n\n\t\t\t&:hover + .wvui-checkbox__icon {\n\t\t\t\tbackground-color: @color-primary--hover;\n\t\t\t\tborder-color: @border-color-input-binary--hover;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Styles for when `label` is active (being pressed).\n\t&:active .wvui-checkbox__input:enabled {\n\t\t& + .wvui-checkbox__icon {\n\t\t\tbackground-color: @background-color-input-binary--active;\n\t\t\tborder-color: @border-color-input-binary--active;\n\t\t\tbox-shadow: @box-shadow-input-binary--active;\n\t\t}\n\n\t\t&:checked + .wvui-checkbox__icon {\n\t\t\tbackground-color: @background-color-input-binary--active;\n\t\t\tborder-color: @border-color-input-binary--active;\n\t\t\tbox-shadow: @box-shadow-input-binary--active;\n\t\t}\n\t}\n}\n</style>\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/icon/Icon.stories.ts","messages":[{"ruleId":"vue/one-component-per-file","severity":1,"message":"There is more than one component in this file.","line":122,"column":14,"nodeType":"ObjectExpression","messageId":"toManyComponents","endLine":151,"endColumn":3},{"ruleId":"vue/one-component-per-file","severity":1,"message":"There is more than one component in this file.","line":154,"column":14,"nodeType":"ObjectExpression","messageId":"toManyComponents","endLine":207,"endColumn":3}],"errorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import Vue from 'vue';\nimport { Args, ArgType, StoryContext } from '@storybook/addons';\nimport WvuiIcon from './Icon.vue';\nimport { AnyIcon } from './iconTypes';\nimport * as icons from '../../themes/icons';\nimport { filterKeys, makeActionArgTypes, makeActionListeners } from '../../utils/StoryUtils';\n\n// Utilities\n\n/**\n * ArgType options for an icon dropdown.\n *\n * @example\n *     argTypes: {\n *         icon: {\n *             ...makeIconArgType(),\n *             defaultValue: 'wvuiIconAdd'\n *         }\n *     }\n * @return Object that can be spread into an ArgType object\n */\nexport function makeIconArgType() : ArgType {\n\treturn {\n\t\tcontrol: {\n\t\t\ttype: 'select'\n\t\t},\n\t\toptions: Object.keys( icons )\n\t\t// TODO use mapping here once https://github.com/storybookjs/storybook/issues/14420 is fixed\n\t};\n}\n\n/**\n * Like makeIconArgType(), but with a '(none)' option.\n *\n * @return Object that can be spread into an ArgType object\n */\nexport function makeOptionalIconArgType() : ArgType {\n\treturn {\n\t\tcontrol: {\n\t\t\ttype: 'select'\n\t\t},\n\t\toptions: [ '(none)' ].concat( Object.keys( icons ) ),\n\t\tdefaultValue: '(none)'\n\t\t// TODO use mapping here once https://github.com/storybookjs/storybook/issues/14420 is fixed\n\t};\n}\n\n/**\n * Map an icon name to its icon object.\n *\n * This is typically used to map the value of an icon argtype to an actual icon.\n *\n * @example\n *    computed: {\n *         iconData() {\n *             return lookupIcon( this.icon );\n *         }\n *    },\n *    template: `\n *        <wvui-some-component :icon=\"iconData\" v-bind=\"$props\" />\n *    `\n * @param iconName Name of an icon\n * @return The icon object for that icon name, or an empty string if there is no such icon\n */\nexport function lookupIcon( iconName: string ) : AnyIcon {\n\t// TODO remove this as obsolete once https://github.com/storybookjs/storybook/issues/14420 is fixed\n\treturn icons[ iconName as keyof typeof icons ] || '';\n}\n\nexport default {\n\ttitle: 'Components/Icon',\n\tcomponent: WvuiIcon,\n\targTypes: {\n\t\ticon: {\n\t\t\t...makeIconArgType(),\n\t\t\tdefaultValue: 'wvuiIconBook'\n\t\t},\n\t\ticonTitle: {\n\t\t\tcontrol: 'text'\n\t\t},\n\t\tlangCode: {\n\t\t\tcontrol: 'select',\n\t\t\t// eslint-disable-next-line es/no-object-values\n\t\t\toptions: Object.values( icons )\n\t\t\t\t// Gather all language codes that appear in a langCodeMap or shouldFlipExceptions\n\t\t\t\t.map( ( icon ) =>\n\t\t\t\t\ttypeof icon !== 'string' && (\n\t\t\t\t\t\t( 'langCodeMap' in icon && Object.keys( icon.langCodeMap ) ) ||\n\t\t\t\t\t\t( 'shouldFlipExceptions' in icon && icon.shouldFlipExceptions )\n\t\t\t\t\t) || []\n\t\t\t\t)\n\t\t\t\t// Flatten this array and add 'en'\n\t\t\t\t.reduce( ( a, b ) => a.concat( b ), [ 'en' ] )\n\t\t\t\t// Remove duplicates\n\t\t\t\t.sort()\n\t\t\t\t.reduce( ( a, b ) => b === a[ a.length - 1 ] ? a : a.concat( [ b ] ),\n\t\t\t\t\t[] as string[] ),\n\t\t\tdefaultValue: 'en'\n\t\t},\n\t\tdir: {\n\t\t\toptions: [ 'ltr', 'rtl' ],\n\t\t\tcontrol: {\n\t\t\t\ttype: 'inline-radio',\n\t\t\t\tlabels: {\n\t\t\t\t\tltr: 'Left to right (ltr)',\n\t\t\t\t\trtl: 'Right to left (rtl)'\n\t\t\t\t}\n\t\t\t},\n\t\t\tdefaultValue: 'ltr',\n\t\t\tdescription: 'Direction (HTML dir attribute)'\n\t\t},\n\t\t...makeActionArgTypes( [ 'click' ] )\n\t},\n\tparameters: {\n\t\tlayout: 'centered'\n\t},\n\t// Prevent the utility functions above from being seen as stories\n\tincludeStories: /^[A-Z]/\n};\n\nexport const Configurable = ( args: Args, { argTypes } : StoryContext ) : Vue.Component =>\n\tVue.extend( {\n\t\tcomponents: { WvuiIcon },\n\t\tprops: Object.keys( argTypes ),\n\t\tcomputed: {\n\t\t\ticonData() : AnyIcon {\n\t\t\t\treturn lookupIcon( this.icon as string );\n\t\t\t},\n\t\t\tactionListeners() {\n\t\t\t\treturn makeActionListeners( args, argTypes );\n\t\t\t},\n\t\t\tfilteredProps() {\n\t\t\t\treturn filterKeys( this.$props, [ 'dir' ] );\n\t\t\t}\n\t\t},\n\t\t// HACK: the Icon component computes its dir at mount time, so changing the dir value\n\t\t// later doesn't cause it to react, breaking flipping for IconVariedByDir icons (although\n\t\t// flipping does work for IconFlipForRTL icons, since that's CSS-based).\n\t\t// To work around this, change the \"key\" attribute when changing dir, which causes Vue\n\t\t// to rerender the component and rerun the mounted() lifecycle hook.\n\t\ttemplate: `\n\t\t\t<div :dir=\"dir\">\n\t\t\t\t<wvui-icon\n\t\t\t\t\t:key=\"dir\"\n\t\t\t\t\t:icon=\"iconData\"\n\t\t\t\t\tv-bind=\"filteredProps\"\n\t\t\t\t\tv-on=\"actionListeners\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t`\n\t} );\n\nexport const AllIcons = ( _args: Args, { argTypes } : StoryContext ) : Vue.Component =>\n\tVue.extend( {\n\t\tcomponents: { WvuiIcon },\n\t\tprops: Object.keys( argTypes ),\n\t\tcomputed: {\n\t\t\tflattenedIcons() {\n\t\t\t\tconst filteredProps = filterKeys( this.$props, [ 'dir' ] );\n\t\t\t\tconst flattened = [];\n\t\t\t\tfor ( const iconName in icons ) {\n\t\t\t\t\tconst icon = lookupIcon( iconName );\n\t\t\t\t\tif (\n\t\t\t\t\t\ttypeof icon !== 'string' &&\n\t\t\t\t\t\t( 'langCodeMap' in icon || 'shouldFlipExceptions' in icon )\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst langCodes = 'langCodeMap' in icon ? Object.keys( icon.langCodeMap ) :\n\t\t\t\t\t\t\ticon.shouldFlipExceptions || [];\n\t\t\t\t\t\tfor ( const langCode of langCodes ) {\n\t\t\t\t\t\t\tflattened.push( {\n\t\t\t\t\t\t\t\tlabel: `${iconName} (${langCode})`,\n\t\t\t\t\t\t\t\tprops: { ...filteredProps, icon, langCode }\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t}\n\t\t\t\t\t\tflattened.push( {\n\t\t\t\t\t\t\tlabel: `${iconName} (other languages)`,\n\t\t\t\t\t\t\tprops: { ...filteredProps, icon, langCode: '' }\n\t\t\t\t\t\t} );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tflattened.push( {\n\t\t\t\t\t\t\tlabel: iconName,\n\t\t\t\t\t\t\tprops: { ...filteredProps, icon }\n\t\t\t\t\t\t} );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn flattened;\n\t\t\t}\n\t\t},\n\t\ttemplate: `\n\t\t\t<table>\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th scope=\"col\"></th>\n\t\t\t\t\t\t<th scope=\"col\">LTR</th>\n\t\t\t\t\t\t<th scope=\"col\">RTL</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t<tr v-for=\"icon in flattenedIcons\">\n\t\t\t\t\t\t<td>{{ icon.label }}</td>\n\t\t\t\t\t\t<td dir=\"ltr\"><wvui-icon v-bind=\"icon.props\" /></td>\n\t\t\t\t\t\t<td dir=\"rtl\"><wvui-icon v-bind=\"icon.props\" /></td>\n\t\t\t\t\t</tr>\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t`\n\t} );\n\nAllIcons.argTypes = {\n\ticon: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t},\n\tlangCode: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t},\n\tdir: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t}\n};\n\nAllIcons.parameters = {\n\tdocs: {\n\t\tsource: {\n\t\t\ttype: 'code'\n\t\t}\n\t}\n};\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/icon/Icon.test.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"jest/no-try-expect","replacedBy":["no-conditional-expect"]},{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/icon/Icon.vue","messages":[{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":17,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":17,"endColumn":37},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":18,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":18,"endColumn":64},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":31,"column":1,"nodeType":"ExportDefaultDeclaration","messageId":"forbidden","endLine":87,"endColumn":5},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":84,"column":3,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":84,"endColumn":61},{"ruleId":"es/no-optional-chaining","severity":2,"message":"ES2020 optional chaining is forbidden.","line":85,"column":27,"nodeType":"Punctuator","messageId":"forbidden","endLine":85,"endColumn":29}],"errorCount":5,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<span class=\"wvui-icon\" :class=\"rootClasses\" @click=\"onClick\">\n\t\t<svg\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\twidth=\"20\"\n\t\t\theight=\"20\"\n\t\t\tviewBox=\"0 0 20 20\"\n\t\t\t:aria-hidden=\"lacksTitle\"\n\t\t>\n\t\t\t<title v-if=\"iconTitle\">{{ iconTitle }}</title>\n\t\t\t<path fill=\"currentColor\" :d=\"iconPath\" />\n\t\t</svg>\n\t</span>\n</template>\n\n<script lang=\"ts\">\nimport Vue, { PropType } from 'vue';\nimport { AnyIcon, getIconPath, shouldFlip } from './iconTypes';\n\n/**\n * SVG icon.\n *\n * See src/themes/icons.ts for a list of all icons. To use an icon, import it,\n * assign it to a name in your component's data option, then use v-bind\n * to set the icon attribute of the <wvui-icon> element to that name.\n *\n * Alternately, custom or third-party icons could be used as long as the icon\n * prop provided to this component is either a string containing the icon's SVG\n * path or one of the icon types described in ./iconTypes.ts.\n */\nexport default Vue.extend( {\n\tname: 'WvuiIcon',\n\tprops: {\n\t\t/** The SVG path or an object containing that path plus other data. */\n\t\ticon: {\n\t\t\ttype: [ String, Object ] as PropType<AnyIcon>,\n\t\t\trequired: true\n\t\t},\n\t\t/**\n\t\t * Accessible title for SVG. String or message object. If not included,\n\t\t * the SVG will be hidden from screen readers via aria-hidden=\"true\".\n\t\t */\n\t\ticonTitle: {\n\t\t\ttype: [ String, Object ],\n\t\t\tdefault: ''\n\t\t},\n\t\t/**\n\t\t * Explicitly set the current HTMLElement.lang. See\n\t\t * https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/lang.\n\t\t * Defaults to the document lang.\n\t\t */\n\t\tlangCode: {\n\t\t\ttype: String,\n\t\t\tdefault: function () { return document.documentElement.lang; }\n\t\t}\n\t},\n\tdata: function (): Record<string, string> {\n\t\treturn {\n\t\t\t// Initially, use the document dir. Once the component mounts, we'll\n\t\t\t// check the element's computed style and update dir if needed.\n\t\t\tdir: document.documentElement.dir\n\t\t};\n\t},\n\tcomputed: {\n\t\trootClasses: function (): Record<string, boolean> {\n\t\t\treturn {\n\t\t\t\t'wvui-icon--flip-for-rtl': shouldFlip( this.icon, this.langCode )\n\t\t\t};\n\t\t},\n\t\tlacksTitle: function (): boolean {\n\t\t\treturn !this.iconTitle;\n\t\t},\n\t\ticonPath: function (): string {\n\t\t\treturn getIconPath( this.icon, this.langCode, this.dir );\n\t\t}\n\t},\n\tmethods: {\n\t\tonClick: function ( event: Event ): void {\n\t\t\tthis.$emit( 'click', event );\n\t\t}\n\t},\n\tmounted: function () {\n\t\t// Now that the component is mounted, check its computed style and update dir\n\t\tconst computedStyle = window.getComputedStyle( this.$el );\n\t\tthis.dir = computedStyle?.direction || this.dir;\n\t}\n} );\n</script>\n\n<style lang=\"less\">\n@import ( reference ) '@/themes/wikimedia-ui.less';\n\n.wvui-icon {\n\t// Set the default icon color; callers that want a different color should override this rule\n\tcolor: @color-base;\n\t// Maintain an inline outer element while using flexbox to center the SVG\n\t// and avoid extra space around the image.\n\tdisplay: inline-flex; /* stylelint-disable-line plugin/no-unsupported-browser-features */\n\talign-items: center;\n\tjustify-content: center;\n\t// For inline, inline-block, and table layouts.\n\tvertical-align: middle;\n}\n\n// Horizontally flip icons that should be flipped for RTL languages.\n[ dir='rtl' ] .wvui-icon--flip-for-rtl svg {\n\ttransform: scaleX( -1 );\n}\n</style>\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/icon/iconTypes.test.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"jest/no-try-expect","replacedBy":["no-conditional-expect"]},{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/icon/iconTypes.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/input/Input.stories.ts","messages":[{"ruleId":"vue/one-component-per-file","severity":1,"message":"There is more than one component in this file.","line":43,"column":14,"nodeType":"ObjectExpression","messageId":"toManyComponents","endLine":67,"endColumn":3},{"ruleId":"vue/one-component-per-file","severity":1,"message":"There is more than one component in this file.","line":70,"column":14,"nodeType":"ObjectExpression","messageId":"toManyComponents","endLine":110,"endColumn":3},{"ruleId":"vue/one-component-per-file","severity":1,"message":"There is more than one component in this file.","line":168,"column":14,"nodeType":"ObjectExpression","messageId":"toManyComponents","endLine":180,"endColumn":3}],"errorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import Vue from 'vue';\nimport { Args, StoryContext } from '@storybook/addons';\nimport WvuiInput from './Input.vue';\nimport WvuiButton from '../button/Button.vue';\nimport { InputType } from './InputType';\nimport { wvuiIconInfoFilled, wvuiIconSearch } from '../../themes/icons';\nimport { makeActionArgTypes, makeActionListeners } from '../../utils/StoryUtils';\nimport { makeOptionalIconArgType, lookupIcon } from '../icon/Icon.stories';\nimport './Input.stories.less';\n\nexport default {\n\ttitle: 'Components/Input',\n\tcomponent: WvuiInput,\n\tparameters: {\n\t\tlayout: 'centered'\n\t},\n\targTypes: {\n\t\tvalue: {\n\t\t\tcontrol: 'text'\n\t\t},\n\t\ttype: {\n\t\t\tcontrol: {\n\t\t\t\ttype: 'inline-radio',\n\t\t\t\t// eslint-disable-next-line es/no-object-values\n\t\t\t\toptions: Object.values( InputType )\n\t\t\t},\n\t\t\tdefaultValue: InputType.Text\n\t\t},\n\t\tstartIcon: makeOptionalIconArgType(),\n\t\tendIcon: makeOptionalIconArgType(),\n\t\tplaceholder: {\n\t\t\tcontrol: 'text',\n\t\t\tdefaultValue: 'Type something',\n\t\t\ttable: {\n\t\t\t\tcategory: 'Attributes'\n\t\t\t}\n\t\t},\n\t\t...makeActionArgTypes( [ 'input', 'focus', 'blur', 'change' ] )\n\t}\n};\n\nexport const Configurable = ( args: Args, { argTypes } : StoryContext ) : Vue.Component =>\n\tVue.extend( {\n\t\tcomponents: { WvuiInput },\n\t\tprops: Object.keys( argTypes ),\n\t\tcomputed: {\n\t\t\tstartIconData() {\n\t\t\t\treturn lookupIcon( this.startIcon );\n\t\t\t},\n\t\t\tendIconData() {\n\t\t\t\treturn lookupIcon( this.endIcon );\n\t\t\t},\n\t\t\tactionListeners() {\n\t\t\t\treturn makeActionListeners( args, argTypes );\n\t\t\t}\n\t\t},\n\t\ttemplate: `\n\t\t\t<div class=\"sb-input\">\n\t\t\t\t<wvui-input\n\t\t\t\t\t:startIcon=\"startIconData\"\n\t\t\t\t\t:endIcon=\"endIconData\"\n\t\t\t\t\tv-bind=\"$props\"\n\t\t\t\t\tv-on=\"actionListeners\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t`\n\t} );\n\nexport const CommonUses = ( _args: Args, { argTypes } : StoryContext ) : Vue.Component =>\n\tVue.extend( {\n\t\tcomponents: { WvuiInput },\n\t\tprops: Object.keys( argTypes ),\n\t\tdata: () => ( {\n\t\t\tsearchIcon: wvuiIconSearch,\n\t\t\tinfoFilledIcon: wvuiIconInfoFilled,\n\t\t\tInputType\n\t\t} ),\n\t\ttemplate: `\n\t\t\t<div class=\"sb-input\">\n\t\t\t\t<p>\n\t\t\t\t\tWith start icon:\n\t\t\t\t\t<wvui-input\n\t\t\t\t\t\t:disabled=\"disabled\"\n\t\t\t\t\t\t:type=\"InputType.Search\"\n\t\t\t\t\t\t:start-icon=\"searchIcon\"\n\t\t\t\t\t\tplaceholder=\"Search…\"\n\t\t\t\t\t/>\n\t\t\t\t</p>\n\t\t\t\t<p>\n\t\t\t\t\tWith end icon:\n\t\t\t\t\t<wvui-input\n\t\t\t\t\t\t:disabled=\"disabled\"\n\t\t\t\t\t\t:type=\"InputType.Search\"\n\t\t\t\t\t\t:end-icon=\"infoFilledIcon\"\n\t\t\t\t\t\tplaceholder=\"Search…\"\n\t\t\t\t\t/>\n\t\t\t\t</p>\n\t\t\t\t<p>\n\t\t\t\t\tWith clear action:\n\t\t\t\t\t<wvui-input\n\t\t\t\t\t\t:disabled=\"disabled\"\n\t\t\t\t\t\t:type=\"InputType.Search\"\n\t\t\t\t\t\tplaceholder=\"Type something…\"\n\t\t\t\t\t\tclearable\n\t\t\t\t\t\tvalue=\"Some text\"\n\t\t\t\t\t/>\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t`\n\t} );\n\n// Disable everything except \"disabled\"\n// TODO there has to be a better way to do this\nCommonUses.argTypes = {\n\ttype: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t},\n\tvalue: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t},\n\tstartIcon: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t},\n\tendIcon: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t},\n\tclearable: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t},\n\tplaceholder: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t},\n\tinput: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t},\n\tfocus: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t},\n\tblur: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t},\n\tchange: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t}\n};\n\nexport const WithButton = ( args: Args, context: StoryContext ) : Vue.Component =>\n\tVue.extend( {\n\t\tcomponents: {\n\t\t\tStoryConfigurable: Configurable( args, context ),\n\t\t\tWvuiButton\n\t\t},\n\t\tprops: Object.keys( context.argTypes ),\n\t\ttemplate: `\n\t\t\t<div class=\"sb-input--has-button\">\n\t\t\t\t<story-configurable v-bind=\"$props\" />\n\t\t\t\t<wvui-button :disabled=\"disabled\">Search</wvui-button>\n\t\t\t</div>\n\t\t`\n\t} );\n\nWithButton.argTypes = {\n\tstartIcon: {\n\t\tdefaultValue: 'wvuiIconSearch'\n\t},\n\tclearable: {\n\t\tdefaultValue: true\n\t},\n\tplaceholder: {\n\t\tdefaultValue: 'Search…'\n\t}\n};\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/input/Input.test.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"jest/no-try-expect","replacedBy":["no-conditional-expect"]},{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/input/Input.vue","messages":[{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":34,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":34,"endColumn":54},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":35,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":35,"endColumn":37},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":36,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":36,"endColumn":41},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":37,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":37,"endColumn":45},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":38,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":38,"endColumn":52},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":40,"column":1,"nodeType":"ExportDefaultDeclaration","messageId":"forbidden","endLine":144,"endColumn":5},{"ruleId":"vue/no-boolean-default","severity":2,"message":"Boolean prop should not set a default (Vue defaults it to false).","line":61,"column":4,"nodeType":"Property","endLine":61,"endColumn":18},{"ruleId":"vue/no-boolean-default","severity":2,"message":"Boolean prop should not set a default (Vue defaults it to false).","line":80,"column":4,"nodeType":"Property","endLine":80,"endColumn":18},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":114,"column":4,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":114,"endColumn":52},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":115,"column":4,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":115,"endColumn":29},{"ruleId":"es/no-destructuring","severity":2,"message":"ES2015 destructuring is forbidden.","line":115,"column":10,"nodeType":"ObjectPattern","messageId":"forbidden","endLine":115,"endColumn":19}],"errorCount":11,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<div\n\t\tclass=\"wvui-input\"\n\t\t:class=\"rootClasses\"\n\t>\n\t\t<input\n\t\t\tref=\"input\"\n\t\t\tdir=\"auto\"\n\t\t\tclass=\"wvui-input__input\"\n\t\t\tv-bind=\"$attrs\"\n\t\t\t:disabled=\"disabled\"\n\t\t\t:type=\"type\"\n\t\t\t:value=\"computedValue\"\n\t\t\t@input=\"onInput\"\n\t\t\t@change=\"onChange\"\n\t\t\t@focus=\"onFocus\"\n\t\t\t@blur=\"onBlur\"\n\t\t>\n\t\t<wvui-icon\n\t\t\tv-if=\"startIcon\"\n\t\t\t:icon=\"startIcon\"\n\t\t\tclass=\"wvui-input__start-icon\"\n\t\t/>\n\t\t<wvui-icon\n\t\t\tv-if=\"isClearable || endIcon\"\n\t\t\t:icon=\"endIcon || clearIcon\"\n\t\t\tclass=\"wvui-input__end-icon\"\n\t\t\t@click=\"onEndIconClick\"\n\t\t/>\n\t</div>\n</template>\n\n<script lang=\"ts\">\nimport { InputType, isInputType } from './InputType';\nimport Vue, { PropType } from 'vue';\nimport WvuiIcon from '../icon/Icon.vue';\nimport { AnyIcon } from '../icon/iconTypes';\nimport { wvuiIconClear } from '../../themes/icons';\n\nexport default Vue.extend( {\n\tname: 'WvuiInput',\n\tcomponents: { WvuiIcon: WvuiIcon },\n\t/**\n\t * All attributes set on the components such as disabled and type are passed to the underlying\n\t * input.\n\t */\n\tinheritAttrs: false,\n\tprops: {\n\t\tvalue: {\n\t\t\ttype: [ String, Number ],\n\t\t\tdefault: ''\n\t\t},\n\t\ttype: {\n\t\t\ttype: String as PropType<InputType>,\n\t\t\tdefault: InputType.Text,\n\t\t\t// use arrow function for type inference of property\n\t\t\tvalidator: function ( value ) { return isInputType( value ); }\n\t\t},\n\t\tdisabled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false\n\t\t},\n\t\t/** An icon at the start of the input element. Similar to a ::before pseudo-element. */\n\t\tstartIcon: {\n\t\t\ttype: [ String, Object ] as PropType<AnyIcon | undefined>,\n\t\t\tdefault: undefined\n\t\t},\n\t\t/** An icon at the end of the input element. Similar to an ::after pseudo-element. */\n\t\tendIcon: {\n\t\t\ttype: [ String, Object ] as PropType<AnyIcon | undefined>,\n\t\t\tdefault: undefined\n\t\t},\n\t\t/**\n\t\t * Override end icon with a clear button at the end of the input element. When clear is\n\t\t * pressed the input's contents is deleted. The elements automatically hides and appears\n\t\t * based on input state.\n\t\t */\n\t\tclearable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false\n\t\t}\n\t},\n\tdata: function (): Record<string, string | number | AnyIcon> {\n\t\treturn {\n\t\t\tnewValue: this.value,\n\t\t\tclearIcon: wvuiIconClear\n\t\t};\n\t},\n\tcomputed: {\n\t\tisClearable: function (): boolean {\n\t\t\treturn this.clearable &&\n\t\t\t\t!!this.computedValue &&\n\t\t\t\t!this.disabled;\n\t\t},\n\t\trootClasses: function (): Record<string, boolean> {\n\t\t\treturn {\n\t\t\t\t'wvui-input--has-start-icon': !!this.startIcon,\n\t\t\t\t'wvui-input--has-end-icon': !!this.endIcon || this.clearable,\n\t\t\t\t'wvui-input--clearable': this.clearable\n\t\t\t};\n\t\t},\n\t\tcomputedValue: {\n\t\t\tget: function (): string | number {\n\t\t\t\treturn this.newValue as string | number;\n\t\t\t},\n\t\t\tset: function ( value: string | number ) {\n\t\t\t\tthis.newValue = value;\n\t\t\t\tthis.$emit( 'input', value );\n\t\t\t}\n\t\t}\n\t},\n\tmethods: {\n\t\tonInput: function ( event: InputEvent ): void {\n\t\t\tconst target = event.target as HTMLInputElement;\n\t\t\tconst { value } = target;\n\n\t\t\tthis.setCurrentValue( value );\n\t\t},\n\t\tonChange: function ( event: Event ): void {\n\t\t\tthis.$emit( 'change', event );\n\t\t},\n\t\tonFocus: function ( event: FocusEvent ): void {\n\t\t\tthis.$emit( 'focus', event );\n\t\t},\n\t\tonBlur: function ( event: FocusEvent ): void {\n\t\t\tthis.$emit( 'blur', event );\n\t\t},\n\t\tonEndIconClick: function (): void {\n\t\t\tif ( this.clearable ) {\n\t\t\t\tthis.setCurrentValue( '' );\n\t\t\t\tthis.$emit( 'input', '' );\n\t\t\t}\n\t\t},\n\t\tsetCurrentValue: function ( value: string | number ): void {\n\t\t\tthis.computedValue = value;\n\t\t}\n\t},\n\twatch: {\n\t\t// Update input value on v-model change\n\t\tvalue: function ( value: string | number ): void {\n\t\t\tthis.newValue = value;\n\t\t}\n\t}\n} );\n</script>\n\n<style lang=\"less\">\n@import ( reference ) '@/themes/wikimedia-ui.less';\n\n.wvui-input {\n\tposition: relative; // For proper positioning of icons and slotted elements\n\tbox-sizing: border-box;\n\n\t&__start-icon,\n\t&__end-icon {\n\t\tcolor: @color-accessory;\n\t\tposition: absolute;\n\t\ttop: 0;\n\t\tmin-height: @size-icon;\n\t\theight: 100%;\n\t}\n\n\t&__start-icon {\n\t\tleft: @border-width-base;\n\t\twidth: @size-input-icon-container;\n\t\tpointer-events: none;\n\t}\n\n\t&__end-icon {\n\t\tright: @border-width-base;\n\t\twidth: @size-input-icon-container;\n\t}\n\n\t&--clearable {\n\t\t.wvui-input__end-icon {\n\t\t\twidth: calc( @padding-horizontal-input-text * 2 + @size-indicator );\n\t\t\tcursor: pointer;\n\n\t\t\t// stylelint-disable-next-line max-nesting-depth\n\t\t\tsvg {\n\t\t\t\twidth: @size-indicator;\n\t\t\t}\n\t\t}\n\t}\n\n\t&__input {\n\t\tbackground-color: @background-color-base;\n\t\tcolor: @color-base--emphasized;\n\t\tdisplay: block;\n\t\tbox-sizing: border-box;\n\t\twidth: 100%;\n\t\theight: @size-base;\n\t\tmargin: 0;\n\t\tborder: @border-width-base @border-style-base @border-color-base;\n\t\tborder-radius: @border-radius-base;\n\t\tpadding: @padding-input-text;\n\t\tbox-shadow: @box-shadow-base;\n\t\tfont-family: inherit;\n\t\tfont-size: inherit;\n\t\tline-height: @line-height-component;\n\n\t\t&:not( [ disabled ] ) {\n\t\t\ttransition: border-color @transition-duration-medium, box-shadow @transition-duration-medium;\n\n\t\t\t&:hover {\n\t\t\t\tborder-color: @border-color-input--hover;\n\t\t\t}\n\n\t\t\t&:focus {\n\t\t\t\tborder-color: @border-color-base--focus;\n\t\t\t\tbox-shadow: @box-shadow-base--focus;\n\t\t\t\toutline: 0;\n\t\t\t}\n\n\t\t\t&:focus ~ .wvui-input__start-icon,\n\t\t\t&:focus ~ .wvui-input__end-icon {\n\t\t\t\topacity: 1;\n\t\t\t}\n\t\t}\n\n\t\t&[ disabled ] {\n\t\t\tbackground-color: @background-color-base--disabled;\n\t\t\tcolor: @color-placeholder;\n\t\t\t-webkit-text-fill-color: @color-placeholder;\n\t\t\tborder-color: @border-color-base--disabled;\n\t\t\ttext-shadow: @text-shadow-base--disabled;\n\n\t\t\t// stylelint-disable max-nesting-depth, no-descending-specificity\n\t\t\t& ~ .wvui-input__start-icon,\n\t\t\t& ~ .wvui-input__end-icon {\n\t\t\t\topacity: @opacity-base--disabled;\n\t\t\t\tpointer-events: none;\n\t\t\t}\n\t\t\t// stylelint-enable, max-nesting-depth, no-descending-specificity\n\t\t}\n\n\t\t&::placeholder {\n\t\t\tcolor: @color-placeholder;\n\t\t\topacity: 1;\n\t\t}\n\n\t\t// Support IE 10-11, and Edge 12+: Hide proprietary pseudo-element.\n\t\t// See https://developer.mozilla.org/en-US/docs/Web/CSS/::-ms-clear\n\t\t&::-ms-clear {\n\t\t\tdisplay: none;\n\t\t}\n\n\t\t&[ type='search' ] {\n\t\t\t// Support Safari/iOS: Normalize by applying `none`,\n\t\t\t// Chrome would accept `textfield` as well.\n\t\t\t/* stylelint-disable plugin/no-unsupported-browser-features */\n\t\t\t/* autoprefixer: ignore next */\n\t\t\t-webkit-appearance: none;\n\t\t\t// Support Firefox.\n\t\t\t/* autoprefixer: ignore next */\n\t\t\t-moz-appearance: textfield;\n\t\t\t/* stylelint-enable plugin/no-unsupported-browser-features */\n\n\t\t\t// Support: Safari, Chrome (Blink).\n\n\t\t\t// stylelint-disable-next-line max-nesting-depth\n\t\t\t&::-webkit-search-decoration,\n\t\t\t&::-webkit-search-cancel-button {\n\t\t\t\tdisplay: none;\n\t\t\t}\n\t\t}\n\t}\n\n\t&--has-start-icon {\n\t\t// stylelint-disable-next-line no-descending-specificity\n\t\t.wvui-input__input {\n\t\t\tpadding-left: @size-input-icon-container;\n\t\t}\n\t}\n\n\t&--has-end-icon {\n\t\t// stylelint-disable-next-line no-descending-specificity\n\t\t.wvui-input__input {\n\t\t\tpadding-right: @size-input-icon-container;\n\t\t}\n\t}\n}\n</style>\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/input/InputType.test.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"jest/no-try-expect","replacedBy":["no-conditional-expect"]},{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/input/InputType.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/radio/Radio.stories.ts","messages":[{"ruleId":"vue/one-component-per-file","severity":1,"message":"There is more than one component in this file.","line":39,"column":14,"nodeType":"ObjectExpression","messageId":"toManyComponents","endLine":69,"endColumn":3},{"ruleId":"vue/one-component-per-file","severity":1,"message":"There is more than one component in this file.","line":74,"column":14,"nodeType":"ObjectExpression","messageId":"toManyComponents","endLine":112,"endColumn":3}],"errorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import Vue from 'vue';\nimport { Args, StoryContext } from '@storybook/addons';\nimport WvuiRadio from './Radio.vue';\nimport { filterKeys, makeActionArgTypes, makeActionListeners } from '../../utils/StoryUtils';\nimport './Radio.stories.less';\n\nexport default {\n\ttitle: 'Components/Radio',\n\tcomponent: WvuiRadio,\n\tparameters: {\n\t\tlayout: 'centered'\n\t},\n\targTypes: {\n\t\tdefault: {\n\t\t\tcontrol: 'text',\n\t\t\tdefaultValue: 'Configurable radio input'\n\t\t},\n\t\t'v-model': {\n\t\t\tname: 'modelValue',\n\t\t\tcontrol: null\n\t\t},\n\t\tinputValue: {\n\t\t\tcontrol: 'text',\n\t\t\tdefaultValue: 'radio-configurable'\n\t\t},\n\t\tname: {\n\t\t\tcontrol: 'text',\n\t\t\tdefaultValue: 'radio-group'\n\t\t},\n\t\t...makeActionArgTypes( [ 'input' ] )\n\t}\n};\n\n// A single radio, which is useful for demonstrating the configurable parts of\n// the radio component, but not useful for demonstrating the value prop or the\n// on/off state (since it can only be selected once, then it cannot be\n// de-selected).\nexport const Configurable = ( args : Args, { argTypes } : StoryContext ): Vue.Component =>\n\tVue.extend( {\n\t\tcomponents: { WvuiRadio },\n\t\tprops: Object.keys( argTypes ),\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tcurrentValue: ''\n\t\t\t};\n\t\t},\n\t\tcomputed: {\n\t\t\tactionListeners() {\n\t\t\t\treturn makeActionListeners( args, argTypes );\n\t\t\t},\n\t\t\tfilteredProps() {\n\t\t\t\treturn filterKeys( this.$props, [ 'default', 'vModel' ] );\n\t\t\t},\n\t\t\tslotContents() {\n\t\t\t\treturn this.default;\n\t\t\t}\n\t\t},\n\t\ttemplate: `\n\t\t\t<div class=\"sb-radio-wrapper\">\n\t\t\t\t<wvui-radio\n\t\t\t\t\tv-model=\"currentValue\"\n\t\t\t\t\tv-bind=\"filteredProps\"\n\t\t\t\t\tv-on=\"actionListeners\"\n\t\t\t\t>\n\t\t\t\t\t{{ slotContents }}\n\t\t\t\t</wvui-radio>\n\t\t\t</div>\n\t\t`\n\t} );\n\n// A group of radios, useful for demonstrating on/off state and value changes,\n// but not the individual properties of each radio.\nexport const RadioGroup = ( args : Args, { argTypes } : StoryContext ): Vue.Component =>\n\tVue.extend( {\n\t\tcomponents: { WvuiRadio },\n\t\tprops: Object.keys( argTypes ),\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tradioValue: 'radio-2',\n\t\t\t\tradios: [\n\t\t\t\t\t{ label: 'Radio 1', value: 'radio-1', disabled: false },\n\t\t\t\t\t{ label: 'Radio 2 (initially selected)', value: 'radio-2', disabled: false },\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: 'Radio 3, which has a very long label that spans onto a second line',\n\t\t\t\t\t\tvalue: 'radio-3',\n\t\t\t\t\t\tdisabled: false\n\t\t\t\t\t},\n\t\t\t\t\t{ label: 'Radio 4 (disabled)', value: 'radio-4', disabled: true }\n\t\t\t\t]\n\t\t\t};\n\t\t},\n\t\tcomputed: {\n\t\t\tactionListeners() {\n\t\t\t\treturn makeActionListeners( args, argTypes );\n\t\t\t}\n\t\t},\n\t\ttemplate: `\n\t\t\t<div class=\"sb-radio-wrapper\">\n\t\t\t\t<wvui-radio\n\t\t\t\t\tv-for=\"radio in radios\"\n\t\t\t\t\t:key=\"'radio-' + radio.value\"\n\t\t\t\t\tv-model=\"radioValue\"\n\t\t\t\t\t:input-value=\"radio.value\"\n\t\t\t\t\t:name=\"$props.name\"\n\t\t\t\t\t:disabled=\"radio.disabled\"\n\t\t\t\t\tv-on=\"actionListeners\"\n\t\t\t\t>\n\t\t\t\t\t{{ radio.label }}\n\t\t\t\t</wvui-radio>\n\t\t\t</div>\n\t\t`\n\t} );\n\nRadioGroup.argTypes = {\n\tdefault: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t},\n\tinputValue: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t},\n\tdisabled: {\n\t\ttable: {\n\t\t\tdisable: true\n\t\t}\n\t}\n};\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/radio/Radio.test.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"jest/no-try-expect","replacedBy":["no-conditional-expect"]},{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/radio/Radio.vue","messages":[{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":25,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":25,"endColumn":23},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":26,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":26,"endColumn":87},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":27,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":27,"endColumn":85},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":43,"column":1,"nodeType":"ExportDefaultDeclaration","messageId":"forbidden","endLine":109,"endColumn":5},{"ruleId":"vue/no-boolean-default","severity":2,"message":"Boolean prop should not set a default (Vue defaults it to false).","line":72,"column":4,"nodeType":"Property","endLine":72,"endColumn":18},{"ruleId":"es/no-destructuring","severity":2,"message":"ES2015 destructuring is forbidden.","line":84,"column":27,"nodeType":"ObjectPattern","messageId":"forbidden","endLine":84,"endColumn":35},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":86,"column":3,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":86,"endColumn":41},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":93,"column":3,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":95,"endColumn":5},{"ruleId":"@typescript-eslint/explicit-function-return-type","severity":1,"message":"Missing return type on function.","line":93,"column":22,"nodeType":"FunctionExpression","messageId":"missingReturnType","endLine":93,"endColumn":33},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":100,"column":3,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":100,"endColumn":54},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":101,"column":3,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":101,"endColumn":63}],"errorCount":10,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<label\n\t\tclass=\"wvui-radio\"\n\t\t:aria-disabled=\"disabled\"\n\t\t@click=\"focusInput\"\n\t>\n\t\t<input\n\t\t\tref=\"input\"\n\t\t\tv-model=\"wrappedModel\"\n\t\t\tclass=\"wvui-radio__input\"\n\t\t\ttype=\"radio\"\n\t\t\t:name=\"name\"\n\t\t\t:value=\"inputValue\"\n\t\t\t:disabled=\"disabled\"\n\t\t>\n\t\t<span class=\"wvui-radio__icon\" :aria-disabled=\"disabled\" />\n\t\t<span class=\"wvui-radio__label-content\">\n\t\t\t<!-- @slot Input label content -->\n\t\t\t<slot />\n\t\t</span>\n\t</label>\n</template>\n\n<script lang=\"ts\">\nimport Vue from 'vue';\nimport VueCompositionAPI, { defineComponent, ref, toRef } from '@vue/composition-api';\nimport useModelWrapper, { modelValueProp } from '../../composables/useModelWrapper';\n\nVue.use( VueCompositionAPI );\n\n/**\n * A binary input that always exists in a group, in which only one input can be\n * on at a time.\n *\n * Typical use will involve using v-for to loop through an array of items and\n * output a Radio component for each one. Each Radio will have the same v-model\n * and name props, but different inputValue props and label content.\n *\n * The v-model value is the inputValue of the Radio that is currently on.\n *\n * @fires {Event} input\n */\nexport default defineComponent( {\n\tname: 'WvuiRadio',\n\tmodel: {\n\t\tprop: 'modelValue',\n\t\tevent: 'input'\n\t},\n\tprops: {\n\t\t/**\n\t\t * Value provided by v-model in a parent component.\n\t\t *\n\t\t * Rather than directly binding a value prop to this component, use\n\t\t * v-model to bind a string, number, or boolean value. This value\n\t\t * represents the value of the radio input that is currently on.\n\t\t */\n\t\tmodelValue: modelValueProp,\n\t\t/**\n\t\t * HTML \"value\" attribute to assign to the input.\n\t\t *\n\t\t * Required for input groups.\n\t\t */\n\t\tinputValue: {\n\t\t\ttype: [ String, Number, Boolean ],\n\t\t\tdefault: false\n\t\t},\n\t\t/**\n\t\t * Whether the disabled attribute should be added to the input.\n\t\t */\n\t\tdisabled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false\n\t\t},\n\t\t/**\n\t\t * HTML \"name\" attribute to assign to the input.\n\t\t *\n\t\t * Required for input groups\n\t\t */\n\t\tname: {\n\t\t\ttype: String,\n\t\t\tdefault: ''\n\t\t}\n\t},\n\tsetup: function ( props, { emit } ) {\n\t\t// Declare template ref.\n\t\tconst input = ref<HTMLInputElement>();\n\n\t\t/**\n\t\t * When the label is clicked, focus on the input.\n\t\t *\n\t\t * This doesn't happen automatically in Firefox or Safari.\n\t\t */\n\t\tconst focusInput = function () {\n\t\t\t( input.value as HTMLInputElement ).focus();\n\t\t};\n\n\t\t// Take the modelValue provided by the parent component via v-model and\n\t\t// generate a wrapped model that we can use for the input element in\n\t\t// this component.\n\t\tconst modelValueRef = toRef( props, 'modelValue' );\n\t\tconst wrappedModel = useModelWrapper( modelValueRef, emit );\n\n\t\treturn {\n\t\t\tinput: input,\n\t\t\tfocusInput: focusInput,\n\t\t\twrappedModel: wrappedModel\n\t\t};\n\t}\n} );\n</script>\n\n<style lang=\"less\">\n@import ( reference ) '@/themes/wikimedia-ui.less';\n@import ( reference ) '@/themes/mixins/binary-input.less';\n\n.wvui-radio {\n\t// Common binary input styles.\n\t.wvui-mixin-binary-input();\n\n\t// Custom-styled radio that's visible to the user.\n\t&__icon {\n\t\tborder-radius: @border-radius-circle;\n\t\ttransition: background-color @transition-base, border-color @transition-base, border-width @transition-base;\n\n\t\t// Add `:focus` state's inner circle.\n\t\t&:before {\n\t\t\tcontent: ' ';\n\t\t\tposition: absolute;\n\t\t\t// `px` unit due to pixel rounding error when using\n\t\t\t// `@size-input-binary / 4`\n\t\t\ttop: -4px;\n\t\t\tright: -4px;\n\t\t\tbottom: -4px;\n\t\t\tleft: -4px;\n\t\t\tborder: @border-width-base @border-style-base transparent;\n\t\t\tborder-radius: @border-radius-circle;\n\t\t}\n\t}\n\n\t// HTML `<input type=\"radio\">`.\n\t// Based on the HTML attributes of the radio input, we can change the style\n\t// of the adjacent `span`, which will look like a custom-styled radio.\n\t&__input {\n\t\t// Note: there is no focus behavior for the input in its unchecked\n\t\t// state because you can't focus on it without selecting it.\n\t\t&:hover + .wvui-radio__icon {\n\t\t\tborder-color: @border-color-input-binary--hover;\n\t\t}\n\n\t\t&:disabled {\n\t\t\t& ~ .wvui-radio__label-content {\n\t\t\t\tcolor: @color-base--disabled;\n\t\t\t}\n\n\t\t\t& + .wvui-radio__icon {\n\t\t\t\tbackground-color: @background-color-filled--disabled;\n\t\t\t\tborder-color: @border-color-base--disabled;\n\t\t\t}\n\n\t\t\t&:checked + .wvui-radio__icon {\n\t\t\t\tbackground-color: @background-color-base;\n\t\t\t\tborder-width: @border-width-input-radio--checked;\n\t\t\t}\n\t\t}\n\n\t\t&:checked:enabled {\n\t\t\t& + .wvui-radio__icon {\n\t\t\t\tborder-width: @border-width-input-radio--checked;\n\t\t\t\tborder-color: @border-color-input-binary--checked;\n\t\t\t}\n\n\t\t\t&:focus + .wvui-radio__icon {\n\t\t\t\t&:before {\n\t\t\t\t\tborder-color: @background-color-base;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t&:hover + .wvui-radio__icon {\n\t\t\t\tborder-color: @border-color-input-binary--hover;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Styles for when `label` is active (being pressed).\n\t&:active &__input:enabled {\n\t\t& + .wvui-radio__icon {\n\t\t\tbackground-color: @background-color-input-binary--active;\n\t\t\tborder-color: @border-color-input-binary--active;\n\t\t}\n\n\t\t&:checked + .wvui-radio__icon {\n\t\t\tbackground-color: @background-color-base;\n\t\t\tborder-color: @border-color-input-binary--active;\n\n\t\t\t&:before {\n\t\t\t\tborder-color: @border-color-input-binary--active;\n\t\t\t}\n\t\t}\n\t}\n}\n</style>\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/typeahead-search/TypeaheadSearch.constants.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/typeahead-search/TypeaheadSearch.stories.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/typeahead-search/TypeaheadSearch.test.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"jest/no-try-expect","replacedBy":["no-conditional-expect"]},{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/typeahead-search/TypeaheadSearch.vue","messages":[{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":102,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":102,"endColumn":37},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":103,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":103,"endColumn":87},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":104,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":104,"endColumn":48},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":105,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":105,"endColumn":47},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":106,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":106,"endColumn":44},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":107,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":107,"endColumn":41},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":108,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":112,"endColumn":48},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":113,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":113,"endColumn":76},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":114,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":114,"endColumn":60},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":115,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":115,"endColumn":96},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":116,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":116,"endColumn":87},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":117,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":117,"endColumn":65},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":119,"column":1,"nodeType":"ExportDefaultDeclaration","messageId":"forbidden","endLine":464,"endColumn":5},{"ruleId":"max-len","severity":1,"message":"This line has a length of 135. Maximum allowed is 100.","line":121,"column":1,"nodeType":"Program","messageId":"max","endLine":121,"endColumn":133},{"ruleId":"vue/no-boolean-default","severity":2,"message":"Boolean prop should not set a default (Vue defaults it to false).","line":161,"column":4,"nodeType":"Property","endLine":161,"endColumn":18},{"ruleId":"vue/no-boolean-default","severity":2,"message":"Boolean prop should not set a default (Vue defaults it to false).","line":169,"column":4,"nodeType":"Property","endLine":169,"endColumn":17},{"ruleId":"vue/no-boolean-default","severity":2,"message":"Boolean prop should not set a default (Vue defaults it to false).","line":173,"column":4,"nodeType":"Property","endLine":173,"endColumn":17},{"ruleId":"vue/no-boolean-default","severity":2,"message":"Boolean prop should not set a default (Vue defaults it to false).","line":177,"column":4,"nodeType":"Property","endLine":177,"endColumn":17},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":276,"column":5,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":276,"endColumn":32},{"ruleId":"es/no-destructuring","severity":2,"message":"ES2015 destructuring is forbidden.","line":295,"column":41,"nodeType":"ObjectPattern","messageId":"forbidden","endLine":295,"endColumn":52},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":299,"column":6,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":302,"endColumn":8},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":355,"column":4,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":358,"endColumn":6},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":386,"column":4,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":386,"endColumn":62},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":406,"column":4,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":406,"endColumn":66},{"ruleId":"es/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":449,"column":4,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":452,"endColumn":6}],"errorCount":24,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<div\n\t\tclass=\"wvui-typeahead-search\"\n\t\t:class=\"rootClasses\"\n\t\trole=\"combobox\"\n\t\t:aria-expanded=\"isExpandedString\"\n\t\taria-haspopup=\"listbox\"\n\t\t:aria-owns=\"suggestionsId\"\n\t\t@mouseover=\"onRootMouseOver\"\n\t\t@mouseout=\"onRootMouseOut\"\n\t\t@keydown.up=\"onKeyDownUp\"\n\t\t@keydown.down=\"onKeyDownDown\"\n\t\t@keydown.escape=\"onKeyDownEscape\"\n\t>\n\t\t<form\n\t\t\t:id=\"id\"\n\t\t\tclass=\"wvui-typeahead-search__form\"\n\t\t\t:action=\"formAction\"\n\t\t\t@submit=\"onSubmit\"\n\t\t>\n\t\t\t<div class=\"wvui-typeahead-search__wrapper\">\n\t\t\t\t<wvui-input\n\t\t\t\t\tclass=\"wvui-typeahead-search__input\"\n\t\t\t\t\t:start-icon=\"startIcon\"\n\t\t\t\t\t:value=\"inputValue\"\n\t\t\t\t\t:type=\"InputType.Search\"\n\t\t\t\t\tname=\"search\"\n\t\t\t\t\tdir=\"auto\"\n\t\t\t\t\tautocapitalize=\"off\"\n\t\t\t\t\tv-bind=\"$attrs\"\n\t\t\t\t\tautocomplete=\"off\"\n\t\t\t\t\taria-autocomplete=\"list\"\n\t\t\t\t\t:aria-controls=\"suggestionsId\"\n\t\t\t\t\t:aria-activedescendant=\"activeSuggestionId\"\n\t\t\t\t\t@input=\"onInput\"\n\t\t\t\t\t@blur=\"onInputBlur\"\n\t\t\t\t\t@focus=\"onInputFocus\"\n\t\t\t\t/>\n\t\t\t\t<!--\n\t\t\t\t\t@slot A slot for passing hidden inputs like\n\t\t\t\t\t<input type=\"hidden\" name=\"language\" value=\"en\">\n\t\t\t\t-->\n\t\t\t\t<slot />\n\t\t\t\t<ol\n\t\t\t\t\t:id=\"suggestionsId\"\n\t\t\t\t\tclass=\"wvui-typeahead-search__suggestions\"\n\t\t\t\t\trole=\"listbox\"\n\t\t\t\t\t:aria-label=\"suggestionsLabel\"\n\t\t\t\t>\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"(suggestion, index) in suggestionsList\"\n\t\t\t\t\t\t:key=\"index\"\n\t\t\t\t\t\trole=\"option\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<wvui-typeahead-suggestion\n\t\t\t\t\t\t\t:id=\"getSuggestionId( suggestion )\"\n\t\t\t\t\t\t\t:key=\"suggestion.id\"\n\t\t\t\t\t\t\t:article-path=\"formAction\"\n\t\t\t\t\t\t\tclass=\"wvui-typeahead-search__suggestion\"\n\t\t\t\t\t\t\t:query=\"searchQuery\"\n\t\t\t\t\t\t\t:active=\"suggestionActiveIndex === index\"\n\t\t\t\t\t\t\t:suggestion=\"suggestion\"\n\t\t\t\t\t\t\t:show-thumbnail=\"showThumbnail\"\n\t\t\t\t\t\t\t:show-description=\"showDescription\"\n\t\t\t\t\t\t\t:highlight-query=\"highlightQuery\"\n\t\t\t\t\t\t\t@mouseover=\"onSuggestionMouseOver( index )\"\n\t\t\t\t\t\t\t@mousedown.native=\"onSuggestionMouseDown\"\n\t\t\t\t\t\t\t@click=\"onSuggestionClick( suggestion )\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</li>\n\t\t\t\t\t<li role=\"option\">\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:id=\"footerId\"\n\t\t\t\t\t\t\tref=\"footer\"\n\t\t\t\t\t\t\ttabindex=\"-1\"\n\t\t\t\t\t\t\tclass=\"wvui-typeahead-search__suggestions__footer\"\n\t\t\t\t\t\t\t:href=\"footerUrl\"\n\t\t\t\t\t\t\t:class=\"footerClasses\"\n\t\t\t\t\t\t\t@mouseover=\"onFooterHover\"\n\t\t\t\t\t\t\t@mousedown=\"onSuggestionMouseDown\"\n\t\t\t\t\t\t\t@click=\"onSuggestionClick()\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<wvui-icon\n\t\t\t\t\t\t\t\tclass=\"wvui-typeahead-search__suggestions-footer-article-icon\"\n\t\t\t\t\t\t\t\t:icon=\"articleIcon\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclass=\"wvui-typeahead-search__suggestions__footer__text\"\n\t\t\t\t\t\t\t>{{ footerSearchText }} <strong>{{ searchQuery }}</strong></span>\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</li>\n\t\t\t\t</ol>\n\t\t\t</div>\n\t\t\t<wvui-button class=\"wvui-typeahead-search__submit\">\n\t\t\t\t{{ buttonLabel }}\n\t\t\t</wvui-button>\n\t\t</form>\n\t</div>\n</template>\n\n<script lang=\"ts\">\nimport Vue, { PropType } from 'vue';\nimport WvuiTypeaheadSuggestion from '../typeahead-suggestion/TypeaheadSuggestion.vue';\nimport { InputType } from '../input/InputType';\nimport WvuiButton from '../button/Button.vue';\nimport WvuiInput from '../input/Input.vue';\nimport WvuiIcon from '../icon/Icon.vue';\nimport {\n\tAbortableSearchFetch,\n\tSearchClient,\n\tSearchResult\n} from '../typeahead-search/http/SearchClient';\nimport { wvuiIconSearch, wvuiIconArticleSearch } from '../../themes/icons';\nimport { restSearchClient } from './http/restSearchClient';\nimport { createDefaultUrlGenerator, UrlGenerator } from '../typeahead-suggestion/UrlGenerator';\nimport { FetchEndEvent, SuggestionClickEvent, SubmitEvent } from './lifecycle-events';\nimport { DEBOUNCE_INTERVAL } from './TypeaheadSearch.constants';\n\nexport default Vue.extend( {\n\tname: 'WvuiTypeaheadSearch',\n\tcomponents: { WvuiTypeaheadSuggestion: WvuiTypeaheadSuggestion, WvuiButton: WvuiButton, WvuiInput: WvuiInput, WvuiIcon: WvuiIcon },\n\n\t// Pass all attributes to input\n\tinheritAttrs: false,\n\n\tprops: {\n\t\tinitialInputValue: {\n\t\t\ttype: String,\n\t\t\tdefault: ''\n\t\t},\n\t\tbuttonLabel: {\n\t\t\ttype: String,\n\t\t\trequired: true\n\t\t},\n\t\tformAction: {\n\t\t\ttype: String,\n\t\t\trequired: true\n\t\t},\n\t\tclient: {\n\t\t\ttype: Object as PropType<SearchClient>,\n\t\t\tdefault: function () { return restSearchClient(); }\n\t\t},\n\t\turlGenerator: {\n\t\t\ttype: Object as PropType<UrlGenerator>,\n\t\t\tdefault: function () { return createDefaultUrlGenerator(); }\n\t\t},\n\t\tdomain: {\n\t\t\ttype: String,\n\t\t\tdefault: 'en.wikipedia.org'\n\t\t},\n\t\tfooterSearchText: {\n\t\t\ttype: String,\n\t\t\trequired: true\n\t\t},\n\t\tsuggestionsLabel: {\n\t\t\ttype: String,\n\t\t\trequired: true\n\t\t},\n\t\tfocused: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false\n\t\t},\n\t\tid: {\n\t\t\ttype: String,\n\t\t\trequired: true\n\t\t},\n\t\tshowThumbnail: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true\n\t\t},\n\t\tshowDescription: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true\n\t\t},\n\t\thighlightQuery: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true\n\t\t}\n\t},\n\tdata: function () {\n\t\treturn {\n\t\t\tstartIcon: wvuiIconSearch,\n\t\t\tarticleIcon: wvuiIconArticleSearch,\n\t\t\tisHovered: false,\n\t\t\tsuggestionActiveIndex: -1,\n\t\t\tsuggestionsList: [] as SearchResult[],\n\t\t\tisFocused: this.focused,\n\t\t\tsearchQuery: '',\n\t\t\tinputValue: this.initialInputValue,\n\t\t\tInputType: InputType,\n\t\t\tisExpanded: false,\n\t\t\trequest: null as AbortableSearchFetch | null,\n\t\t\tdebounceId: null as NodeJS.Timeout | null\n\t\t};\n\t},\n\tcomputed: {\n\t\trootClasses: function (): Record<string, boolean> {\n\t\t\treturn {\n\t\t\t\t'wvui-typeahead-search--active': this.isHovered,\n\t\t\t\t'wvui-typeahead-search--has-value': !!this.searchQuery,\n\t\t\t\t'wvui-typeahead-search--expanded': this.isExpanded,\n\t\t\t\t'wvui-typeahead-search--show-thumbnail': this.showThumbnail\n\t\t\t};\n\t\t},\n\t\tfooterClasses: function (): Record<string, boolean> {\n\t\t\treturn {\n\t\t\t\t'wvui-typeahead-search__suggestions__footer--active':\n\t\t\t\t\tthis.isFooterSelected\n\t\t\t};\n\t\t},\n\t\tfooterUrl: function (): string {\n\t\t\treturn this.urlGenerator.generateUrl( this.searchQuery );\n\t\t},\n\t\tisFooterSelected: function (): boolean {\n\t\t\treturn this.suggestionActiveIndex === this.suggestionsList.length;\n\t\t},\n\t\tsuggestionsId: function (): string {\n\t\t\treturn String( this.id ) + '-suggestions';\n\t\t},\n\t\tactiveSuggestionId: function (): string {\n\t\t\tif ( !this.isExpanded ) {\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tthis.suggestionActiveIndex < 0 ||\n\t\t\t\tthis.suggestionActiveIndex > this.suggestionsList.length\n\t\t\t) {\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\tif ( this.isFooterSelected ) {\n\t\t\t\treturn this.footerId;\n\t\t\t}\n\n\t\t\treturn this.getSuggestionId( this.suggestionsList[ this.suggestionActiveIndex ] );\n\t\t},\n\t\tfooterId: function (): string {\n\t\t\treturn String( this.suggestionsId ) + '-footer';\n\t\t},\n\n\t\tisExpandedString: function (): string {\n\t\t\treturn this.isExpanded ? 'true' : 'false';\n\t\t}\n\t},\n\tmethods: {\n\t\t/**\n\t\t * A convenience method to update those properties that should be updated when new\n\t\t * suggestions are available.\n\t\t *\n\t\t * @param {string} query\n\t\t * @param {SearchResult[]} suggestions\n\t\t */\n\t\tupdateSuggestions: function ( query: string, suggestions: SearchResult[] ) {\n\t\t\tthis.searchQuery = query;\n\t\t\tthis.suggestionsList = suggestions;\n\t\t\tthis.suggestionActiveIndex = -1;\n\t\t\tthis.isExpanded = !!this.searchQuery && this.isFocused;\n\t\t},\n\n\t\t/**\n\t\t * A convenience method to update those properties that should be update when clearing the\n\t\t * suggestions.\n\t\t */\n\t\tclearSuggestions: function () {\n\t\t\tthis.updateSuggestions( '', [] );\n\t\t},\n\n\t\tonInput: function ( value: string ) {\n\t\t\tif ( this.debounceId ) {\n\t\t\t\t// Cancel the last setTimeout callback in case it hasn't executed yet.\n\t\t\t\tclearTimeout( this.debounceId );\n\t\t\t}\n\n\t\t\tthis.debounceId = setTimeout( function () {\n\t\t\t\tconst query = value.trim();\n\n\t\t\t\tif ( this.request ) {\n\t\t\t\t// Cancel the last request before making a new one in case it is still\n\t\t\t\t// pending. This call is expected to be inert if the request has\n\t\t\t\t// already finished.\n\t\t\t\t\tthis.request.abort();\n\t\t\t\t}\n\n\t\t\t\tif ( !query ) {\n\t\t\t\t\tthis.clearSuggestions();\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis.$emit( 'fetch-start' );\n\n\t\t\t\tthis.request = this.client.fetchByTitle( value, this.domain );\n\n\t\t\t\tthis.request.fetch.then( function ( { results } ) {\n\t\t\t\t\tthis.updateSuggestions( query, results );\n\n\t\t\t\t\t// Event\n\t\t\t\t\tconst event: FetchEndEvent = {\n\t\t\t\t\t\tnumberOfResults: results.length,\n\t\t\t\t\t\tquery: query\n\t\t\t\t\t};\n\n\t\t\t\t\tthis.$emit( 'fetch-end', event );\n\t\t\t\t}.bind( this ) )\n\t\t\t\t\t.catch( function () {\n\t\t\t\t\t// Error handling?\n\t\t\t\t\t} );\n\t\t\t}.bind( this ), DEBOUNCE_INTERVAL );\n\t\t},\n\t\tonSuggestionMouseOver: function ( index: number ) {\n\t\t\tthis.suggestionActiveIndex = index;\n\t\t},\n\t\tonInputFocus: function () {\n\t\t\tthis.isHovered = true;\n\t\t\tthis.isFocused = true;\n\t\t\tthis.isExpanded = !!this.searchQuery;\n\t\t},\n\t\tonInputBlur: function () {\n\t\t\tthis.isFocused = false;\n\t\t\tthis.isHovered = false;\n\t\t\tthis.isExpanded = false;\n\t\t},\n\t\tonFooterHover: function () {\n\t\t\tthis.suggestionActiveIndex = this.suggestionsList.length;\n\t\t},\n\t\tonRootMouseOver: function () {\n\t\t\tthis.isHovered = true;\n\t\t},\n\t\tonRootMouseOut: function () {\n\t\t\tthis.isHovered = this.isFocused;\n\t\t\tthis.suggestionActiveIndex = -1;\n\t\t},\n\n\t\t// A click event is only fired after \"a mousedown and mouseup over the same screen\n\t\t// location\" [0].\n\t\t//\n\t\t// A blur event is fired \"when an element loses focus either via the pointing device...\"\n\t\t// [1].\n\t\t//\n\t\t// When the user clicks a search suggestion, the browser will fire a mousedown event and\n\t\t// then a blur event as the input loses focus. Therefore, we cancel the mousedown event so\n\t\t// that the input never loses focus. Moreover, there's a mouseup over the same screen\n\t\t// location, then a click event is fired.\n\t\t//\n\t\t// This solution was written up by Alexey Lebedev here: https://stackoverflow.com/a/10653160\n\t\t//\n\t\t// [0] https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-eventgroupings-mouseevents\n\t\t// [1] https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-eventgroupings-htmlevents-h3\n\t\tonSuggestionMouseDown: function ( event: MouseEvent ) {\n\t\t\tevent.preventDefault();\n\t\t},\n\n\t\tonSuggestionClick: function ( suggestion?: SearchResult ) {\n\t\t\tconst event: SuggestionClickEvent = {\n\t\t\t\tindex: this.suggestionActiveIndex,\n\t\t\t\tnumberOfResults: this.suggestionsList.length\n\t\t\t};\n\n\t\t\t// State updates\n\t\t\tthis.inputValue = suggestion ? suggestion.title : this.searchQuery;\n\t\t\tthis.updateSuggestions( this.inputValue, [] );\n\n\t\t\tthis.isFocused = true;\n\t\t\tthis.isExpanded = false;\n\n\t\t\t// Event\n\t\t\tthis.$emit( 'suggestion-click', event );\n\t\t},\n\n\t\tonKeyDownUp: function ( event: KeyboardEvent ) { this.handleKeyUpDown( event, -1 ); },\n\t\tonKeyDownDown: function ( event: KeyboardEvent ) { this.handleKeyUpDown( event, 1 ); },\n\t\thandleKeyUpDown: function ( event: KeyboardEvent, offset: number ) {\n\t\t\tif ( !this.isFocused || !this.isExpanded ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// By default, when the focuses an input and presses the up key, the caret is moved to\n\t\t\t// the beginning of the input. Cancel the event as we always want the caret to be at the\n\t\t\t// end of the input.\n\t\t\tevent.preventDefault();\n\n\t\t\tthis.nudgeActiveSuggestion( offset );\n\t\t},\n\t\tnudgeActiveSuggestion: function ( offset: number ) {\n\t\t\tlet newSuggestionActiveIndex = this.suggestionActiveIndex;\n\n\t\t\tnewSuggestionActiveIndex += offset;\n\n\t\t\t// Remember that -1 means that no suggestion is currently highlighted. The next\n\t\t\t// suggestion \"above\" is actually the bottommost suggestion.\n\t\t\tif ( newSuggestionActiveIndex < -1 ) {\n\t\t\t\tnewSuggestionActiveIndex = this.suggestionsList.length;\n\n\t\t\t// We use the > operator here (rather than >=) to replicate the behavior of the current\n\t\t\t// search widget in MediaWiki Core - if the user has highlighted the bottommost\n\t\t\t// suggestion, they have to press down _twice_ in order to highlight the topmost\n\t\t\t// suggestion.\n\t\t\t} else if ( newSuggestionActiveIndex > this.suggestionsList.length + 1 ) {\n\t\t\t\tnewSuggestionActiveIndex = 0;\n\t\t\t}\n\n\t\t\tthis.suggestionActiveIndex = newSuggestionActiveIndex;\n\n\t\t\t// Update the input's value\n\t\t\tconst item = this.suggestionsList[ newSuggestionActiveIndex ];\n\n\t\t\tthis.inputValue = item ? item.title : this.searchQuery;\n\t\t},\n\n\t\tonKeyDownEscape: function ( event: KeyboardEvent ) {\n\t\t\tevent.preventDefault();\n\n\t\t\t// Per https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html#kbd_label_textbox,\n\t\t\t// all variations of the Combobox with Listbox Popup pattern should clear the textbox\n\t\t\t// when the escape key is pressed. However, this is not the case in the current search\n\t\t\t// widget in MediaWiki Core.\n\t\t\t// this.inputValue = '';\n\n\t\t\tthis.isExpanded = false;\n\t\t},\n\n\t\t// Per https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html#kbd_label_textbox,\n\t\t// when the user pressed the enter key:\n\t\t//\n\t\t// - If the suggestions list (the listbox) isn't displayed, then nothing should happen; or\n\t\t//\n\t\t// - If the suggestions list is displayed and a suggestion hasn't been highlighted, then\n\t\t//   the first suggestion should be highlighed\n\t\t//\n\t\t// However, the search search widget in MediaWiki Core, simply submits the parent form.\n\t\t//\n\t\t// onKeyDownEnter( _event: KeyboardEvent ) { /* ... */ }\n\n\t\tgetSuggestionId: function ( suggestion: SearchResult ): string {\n\t\t\treturn 'wvui-typeahead-search-suggestion-' + suggestion.id;\n\t\t},\n\n\t\tonSubmit: function ( event: Event ) {\n\t\t\t// When the user presses the enter key, we prevent form\n\t\t\t// submission when the suggestion footer is active.\n\t\t\t// Instead, we directly navigate to `footerUrl` to ensure\n\t\t\t// the link is the same on both mouse and keyboard\n\t\t\tif ( this.suggestionActiveIndex === this.suggestionsList.length ) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\twindow.location.assign( this.footerUrl );\n\t\t\t}\n\n\t\t\tconst submitEvent: SubmitEvent = {\n\t\t\t\tindex: this.suggestionActiveIndex,\n\t\t\t\tnumberOfResults: this.suggestionsList.length\n\t\t\t};\n\n\t\t\tthis.$emit( 'submit', submitEvent );\n\t\t}\n\t},\n\tmounted: function () {\n\t\tif ( this.initialInputValue ) {\n\t\t\t// Programmatic changes to the input don't trigger the input event so we\n\t\t\t// manually call the onInput method here.\n\t\t\tthis.onInput( this.initialInputValue );\n\t\t}\n\t}\n} );\n\n// ESLint will also check the styles below (and in other Single File\n// Components). Because there are numerous max-len error-causing lines from\n// long `calc` styles/variables below, conflicts with competing stylelint\n// rules, and because ESLint ignores `eslint-disable` comments placed in the\n// styles themselves, we regrettably set it here instead.\n//\n// See \"Known Limitations\" from vue-eslint-parser:\n// https://github.com/vuejs/vue-eslint-parser#%EF%B8%8F-known-limitations\n//\n/* eslint-disable max-len */\n</script>\n\n<style lang=\"less\">\n@import ( reference ) '@/themes/wikimedia-ui.less';\n\n.wvui-typeahead-search {\n\t// Add `background-color` as `border` is around input including button.\n\tbackground-color: @background-color-base;\n\t// Border is styled the same as the input border to visually encapsulate\n\t// search submit button.\n\tborder: @border-width-base @border-style-base @border-color-base;\n\tborder-radius: @border-radius-base;\n\n\t&__form {\n\t\t// stylelint-disable-next-line plugin/no-unsupported-browser-features\n\t\tdisplay: flex;\n\t}\n\n\t&__wrapper {\n\t\tflex-grow: 1;\n\t\t// Makes search results start at the same horizontal position as input.\n\t\tposition: relative;\n\t\t// Set negative margin to make button border overlap with\n\t\t// `.wvui-typeahead-search`'s border.\n\t\tmargin: -@border-width-base;\n\t}\n\n\t&__submit {\n\t\topacity: 0;\n\t\tposition: relative;\n\t\t// Prevent submit button from shrinking on smaller viewports, which causes\n\t\t// the button label to overflow.\n\t\tflex-shrink: 0;\n\t\t// Set negative margin to make button border overlap with\n\t\t// `.wvui-typeahead-search`'s border on all but start margin.\n\t\t// The input already has a negative margin which causes part of the button's\n\t\t// border and input's border to intentionally overlap.\n\t\tmargin: -@border-width-base -@border-width-base -@border-width-base 0;\n\t\tborder-top-left-radius: 0;\n\t\tborder-bottom-left-radius: 0;\n\t\ttransition: opacity @transition-duration-base;\n\n\t\t&:hover {\n\t\t\t// Make the button be on top of the input border when the button is hovered.\n\t\t\tz-index: 1;\n\t\t}\n\n\t\t&:focus {\n\t\t\topacity: @opacity-base;\n\t\t\t// Make the button be on top of the input border when the input is\n\t\t\t// hovered while the button is focused.\n\t\t\tz-index: 1;\n\t\t}\n\t}\n\n\t&__suggestions {\n\t\tbackground-color: @background-color-base;\n\t\tlist-style: none;\n\t\tdisplay: none;\n\t\tposition: absolute;\n\t\ttop: @size-base;\n\t\tright: 0;\n\t\tleft: 0;\n\t\tbox-sizing: border-box;\n\t\tmargin: 0;\n\t\tborder: @border-width-base @border-style-base @border-color-base;\n\t\tborder-top-width: 0;\n\t\tborder-radius: 0 0 @border-radius-base @border-radius-base;\n\t\tpadding: 0;\n\t\tbox-shadow: @box-shadow-menu;\n\n\t\t&__footer {\n\t\t\tcolor: @color-base;\n\t\t\t// stylelint-disable-next-line plugin/no-unsupported-browser-features\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tborder-top: @border-width-base @border-style-base @border-color-heading;\n\t\t\tpadding: @padding-vertical-typeahead-suggestion\n\t\t\t\t@padding-horizontal-typeahead-suggestion\n\t\t\t\t@padding-vertical-typeahead-suggestion\n\t\t\t\t@size-input-icon-container;\n\t\t\ttext-decoration: none;\n\t\t\tcursor: pointer;\n\n\t\t\t&:visited,\n\t\t\t&:active {\n\t\t\t\tcolor: @color-base;\n\t\t\t}\n\n\t\t\t// stylelint-disable-next-line max-nesting-depth\n\t\t\t.wvui-icon {\n\t\t\t\tcolor: @color-accessory;\n\t\t\t\t// Because the footer icon should line up vertically with the\n\t\t\t\t// suggestion text when `showThumbnail` is false, we set its width to\n\t\t\t\t// `auto` here instead of using the more intuitive @size-search-figure\n\t\t\t\t// variable so that it doesn't have extra horizontal space.\n\t\t\t\twidth: auto;\n\t\t\t\theight: @size-search-figure;\n\t\t\t\tmargin-right: @padding-horizontal-base;\n\t\t\t}\n\t\t}\n\n\t\t&__footer--active {\n\t\t\tbackground-color: @background-color-base--hover;\n\t\t}\n\n\t\t&__footer__text {\n\t\t\tfont-size: @font-size-typeahead-suggestion-title;\n\t\t}\n\t}\n\n\t&__suggestion {\n\t\tpadding-left: @size-input-icon-container;\n\t}\n\n\t.wvui-input__input {\n\t\tborder-right-color: transparent;\n\t}\n\n\t//\n\t// Rules that alter elements based on a block-level modifier follow.\n\t//\n\t&--has-value {\n\t\t.wvui-input__input {\n\t\t\tborder-bottom-left-radius: 0;\n\t\t}\n\t}\n\n\t&--active,\n\t&:hover {\n\t\t.wvui-typeahead-search__submit {\n\t\t\topacity: @opacity-base;\n\t\t}\n\n\t\t.wvui-typeahead-search__input {\n\t\t\t// Allow the input's border to be on top of the parent's border when\n\t\t\t// focused or hovered.\n\t\t\tz-index: 1;\n\t\t}\n\n\t\t.wvui-input__input {\n\t\t\tborder-top-right-radius: 0;\n\t\t\tborder-bottom-right-radius: 0;\n\t\t}\n\t}\n\n\t&--expanded {\n\t\t.wvui-typeahead-search__suggestions {\n\t\t\tdisplay: block;\n\t\t}\n\t}\n\n\t&--show-thumbnail {\n\t\t// The amount of space between the typeahead search figure's parent\n\t\t// component and the typeahead search figure (input icon container,\n\t\t// suggestion thumbnail, and footer icon container). We want this space to\n\t\t// be uniform so that the figures vertically line up nicely. For pragmatic\n\t\t// reasons, we use the horizontal padding of the typeahead suggestion.\n\t\t@spacing-start-typeahead-search-figure: @padding-horizontal-typeahead-suggestion;\n\t\t// The amount of spacing from the end of the input icon container, typeahead\n\t\t// suggestion thumb, and footer icon container to the start of their\n\t\t// associated text. We need the text to vertically line up nicely.\n\t\t// For pragmatic reasons, we use the spacing from the typeahead suggestion\n\t\t// thumb.\n\t\t@spacing-end-typeahead-search-figure: @margin-end-typeahead-suggestion-thumb;\n\t\t// The amount the width of the input increases when it is focused to allow\n\t\t// for the extra spacing around the search figures. The caret position\n\t\t// should remain in place for the smoothest transition.\n\t\t@size-typeahead-search-focus-addition: @spacing-start-typeahead-search-figure + @spacing-end-typeahead-search-figure;\n\n\t\t.wvui-input__input {\n\t\t\tpadding-left: @size-search-figure;\n\t\t}\n\n\t\t.wvui-input__start-icon {\n\t\t\twidth: @size-search-figure;\n\t\t}\n\n\t\t.wvui-input__input:focus {\n\t\t\tposition: relative;\n\t\t\t// Don't let the input grow over the search button.\n\t\t\tleft: -@size-typeahead-search-focus-addition;\n\t\t\twidth: calc( 100% + @size-typeahead-search-focus-addition );\n\t\t\t// Keep the cursor in the same place on the screen.\n\t\t\tpadding-left: calc( @spacing-start-typeahead-search-figure + @size-search-figure + @spacing-end-typeahead-search-figure );\n\t\t}\n\n\t\t.wvui-input__input:focus + .wvui-input__start-icon {\n\t\t\t// We use @border-width-base here since the input's start icon position\n\t\t\t// is relative to the input's container (which is outside the input's\n\t\t\t// border) when the input has focus.\n\t\t\tleft: -@size-typeahead-search-focus-addition + @spacing-start-typeahead-search-figure + @border-width-base;\n\t\t}\n\n\t\t.wvui-typeahead-search__suggestions {\n\t\t\tleft: -@size-typeahead-search-focus-addition;\n\t\t}\n\n\t\t.wvui-typeahead-search__suggestion {\n\t\t\tpadding-right: @padding-horizontal-typeahead-suggestion;\n\t\t\tpadding-left: @spacing-start-typeahead-search-figure;\n\t\t}\n\n\t\t.wvui-typeahead-search__suggestions__footer {\n\t\t\tpadding-right: @padding-horizontal-typeahead-suggestion;\n\t\t\tpadding-left: @spacing-start-typeahead-search-figure;\n\t\t}\n\n\t\t.wvui-typeahead-search__suggestions__footer__text {\n\t\t\t// stylelint-disable-next-line plugin/no-unsupported-browser-features\n\t\t\thyphens: auto;\n\t\t\t// Legacy `word-wrap`; IE 6-11, Edge 12+, Firefox 3.5+, Chrome 4+, Safari 3.1+,\n\t\t\t//   Opera 11.5+, iOS 3.2+, Android 2.1+\n\t\t\t// `overflow-wrap` is W3 standard, but it doesn't seem as if browser vendors\n\t\t\t//   will abandon `word-wrap` (it has wider support), therefore no duplication.\n\t\t\tword-wrap: break-word;\n\t\t}\n\n\t\t.wvui-typeahead-search__suggestions-footer-article-icon {\n\t\t\t// Prevent the icon container from shrinking when large text is present\n\t\t\tflex-shrink: 0;\n\t\t\twidth: @size-search-figure;\n\t\t}\n\t}\n}\n</style>\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/typeahead-search/http/SearchClient.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/typeahead-search/http/restSearchClient.test.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"jest/no-try-expect","replacedBy":["no-conditional-expect"]},{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/typeahead-search/http/restSearchClient.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/typeahead-search/lifecycle-events/index.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/typeahead-suggestion-title/TypeaheadSuggestionTitle.test.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"jest/no-try-expect","replacedBy":["no-conditional-expect"]},{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/typeahead-suggestion-title/TypeaheadSuggestionTitle.vue","messages":[{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":9,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":9,"endColumn":37},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":10,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":10,"endColumn":70},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":12,"column":1,"nodeType":"ExportDefaultDeclaration","messageId":"forbidden","endLine":44,"endColumn":5}],"errorCount":3,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<span class=\"wvui-typeahead-suggestion__title\">\n\t\t<!--eslint-disable-next-line max-len-->\n\t\t{{ titleChunks[ 0 ] }}<span class=\"wvui-typeahead-suggestion__match\">{{ titleChunks[ 1 ] }}</span>{{ titleChunks[ 2 ] }}\n\t</span>\n</template>\n\n<script lang=\"ts\">\nimport Vue, { PropType } from 'vue';\nimport { splitStringAtMatch } from './TypeaheadSuggestionTitleUtils';\n\nexport default Vue.extend( {\n\tname: 'WvuiTypeaheadSuggestionTitle',\n\tprops: {\n\t\tquery: {\n\t\t\ttype: String as PropType<string>,\n\t\t\tdefault: ''\n\t\t},\n\t\ttitle: {\n\t\t\ttype: String as PropType<string>,\n\t\t\trequired: true\n\t\t},\n\t\thighlightQuery: {\n\t\t\ttype: Boolean as PropType<boolean>,\n\t\t\tdefault: true\n\t\t}\n\t},\n\tcomputed: {\n\t\t/**\n\t\t * If highlighting is enabled, returns the title with the part that matches the query\n\t\t * highlighted. If highlighting is disabled, returns the unmodified title in a form\n\t\t * compatible with the template above.\n\t\t *\n\t\t * @return [ string, string, string ]\n\t\t */\n\t\ttitleChunks: function (): [ string, string, string ] {\n\t\t\tif ( this.highlightQuery ) {\n\t\t\t\treturn splitStringAtMatch( this.query, this.title );\n\t\t\t}\n\n\t\t\treturn [ '', this.title, '' ];\n\t\t}\n\t}\n} );\n</script>\n\n<style lang=\"less\">\n@import ( reference ) '@/themes/wikimedia-ui.less';\n\n.wvui-typeahead-suggestion__title {\n\tcolor: @color-base;\n\tdisplay: block;\n\tmargin: 0 0 2px 0;\n\tfont-size: @font-size-typeahead-suggestion-title;\n\tfont-weight: @font-weight-bold;\n}\n\n.wvui-typeahead-suggestion__match {\n\tfont-weight: @font-weight-normal;\n}\n\n</style>\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/typeahead-suggestion-title/TypeaheadSuggestionTitleUtils.test.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"jest/no-try-expect","replacedBy":["no-conditional-expect"]},{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/typeahead-suggestion-title/TypeaheadSuggestionTitleUtils.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/typeahead-suggestion/TypeaheadSuggestion.stories.ts","messages":[{"ruleId":"vue/one-component-per-file","severity":1,"message":"There is more than one component in this file.","line":35,"column":14,"nodeType":"ObjectExpression","messageId":"toManyComponents","endLine":74,"endColumn":3},{"ruleId":"vue/one-component-per-file","severity":1,"message":"There is more than one component in this file.","line":116,"column":15,"nodeType":"ObjectExpression","messageId":"toManyComponents","endLine":151,"endColumn":4}],"errorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import Vue from 'vue';\nimport { Args, StoryContext } from '@storybook/addons';\nimport WvuiTypeaheadSuggestion from './TypeaheadSuggestion.vue';\nimport { SearchResult } from '../typeahead-search/http/SearchClient';\nimport { filterKeys, makeActionArgTypes, makeActionListeners } from '../../utils/StoryUtils';\nimport './TypeaheadSuggestion.stories.less';\nimport defaultSuggestionsList from '../typeahead-search/mocks/restApi.suggestions.json';\nimport T277256SuggestionsList from '../typeahead-search/mocks/T277256.suggestions.json';\nimport T280982SuggestionsList from '../typeahead-search/mocks/T280982.suggestions.json';\n\nconst firstSuggestion = defaultSuggestionsList.pages[ 1 ] as SearchResult;\n\nexport default {\n\ttitle: 'Components/TypeaheadSuggestion',\n\tcomponent: WvuiTypeaheadSuggestion,\n\targTypes: {\n\t\tquery: {\n\t\t\tdefaultValue: 'co'\n\t\t},\n\t\tsuggestion: {\n\t\t\ttable: {\n\t\t\t\tdisable: true\n\t\t\t}\n\t\t},\n\t\turlGenerator: {\n\t\t\ttable: {\n\t\t\t\tdisable: true\n\t\t\t}\n\t\t},\n\t\t...makeActionArgTypes( [ 'click', 'mouseover' ] )\n\t}\n};\n\nexport const Configurable = ( args: Args, { argTypes } : StoryContext ) : Vue.Component =>\n\tVue.extend( {\n\t\tcomponents: { WvuiTypeaheadSuggestion },\n\t\tprops: Object.keys( argTypes )\n\t\t\t// eslint-disable-next-line no-restricted-syntax\n\t\t\t.filter( ( prop ) => ![ 'urlGenerator', 'suggestion' ].includes( prop ) ),\n\t\tcomputed: {\n\t\t\tsuggestion(): SearchResult {\n\t\t\t\treturn {\n\t\t\t\t\tid: 42,\n\t\t\t\t\tkey: this.suggestionTitle as string,\n\t\t\t\t\ttitle: this.suggestionTitle as string,\n\t\t\t\t\tdescription: this.suggestionDescription as string,\n\t\t\t\t\tthumbnail: this.suggestionHasThumbnail as boolean ?\n\t\t\t\t\t\t{ url: this.suggestionThumbnailUrl as string } : undefined\n\t\t\t\t};\n\t\t\t},\n\t\t\tactionListeners() {\n\t\t\t\treturn makeActionListeners( args, argTypes );\n\t\t\t},\n\t\t\tfilteredProps() {\n\t\t\t\treturn filterKeys( this.$props, [\n\t\t\t\t\t'suggestionTitle',\n\t\t\t\t\t'suggestionDescription',\n\t\t\t\t\t'suggestionHasThumbnail',\n\t\t\t\t\t'suggestionThumbnailUrl'\n\t\t\t\t] );\n\t\t\t}\n\t\t},\n\t\ttemplate: `\n\t\t\t<ol class=\"sb-search__suggestions\" role=\"listbox\">\n\t\t\t\t<li role=\"option\">\n\t\t\t\t\t<wvui-typeahead-suggestion\n\t\t\t\t\t\t:suggestion=\"suggestion\"\n\t\t\t\t\t\tv-bind=\"filteredProps\"\n\t\t\t\t\t\tv-on=\"actionListeners\"\n\t\t\t\t\t/>\n\t\t\t\t</li>\n\t\t\t</ol>\n\t\t`\n\t} );\n\nConfigurable.argTypes = {\n\tsuggestionTitle: {\n\t\tcontrol: 'text',\n\t\tdefaultValue: firstSuggestion.title,\n\t\tdescription: 'Page title in the suggestion data',\n\t\ttable: {\n\t\t\tcategory: 'Suggestion data'\n\t\t}\n\t},\n\tsuggestionDescription: {\n\t\tcontrol: 'text',\n\t\tdefaultValue: firstSuggestion.description,\n\t\tdescription: 'Page description in the suggestion data',\n\t\ttable: {\n\t\t\tcategory: 'Suggestion data'\n\t\t}\n\t},\n\tsuggestionHasThumbnail: {\n\t\tcontrol: 'boolean',\n\t\tdefaultValue: true,\n\t\tdescription: 'Whether the suggestion data contains a thumbnail',\n\t\ttable: {\n\t\t\tcategory: 'Suggestion data'\n\t\t}\n\t},\n\tsuggestionThumbnailUrl: {\n\t\tcontrol: 'text',\n\t\tdefaultValue: firstSuggestion.thumbnail?.url,\n\t\tdescription: 'Thumbnail URL in the suggestion data',\n\t\ttable: {\n\t\t\tcategory: 'Suggestion data'\n\t\t}\n\t}\n};\n\ntype SuggestionsList = { pages: Record<string, unknown>[] };\n\nfunction makeExampleListStory( suggestionsList : SuggestionsList, defaultQuery: string ) :\n\t( ( args: Args, context: StoryContext ) => Vue.Component ) {\n\tconst story = ( args: Args, { argTypes } : StoryContext ) : Vue.Component =>\n\t\tVue.extend( {\n\t\t\tcomponents: { WvuiTypeaheadSuggestion },\n\t\t\tprops: Object.keys( argTypes )\n\t\t\t\t// eslint-disable-next-line no-restricted-syntax\n\t\t\t\t.filter( ( prop ) => ![ 'urlGenerator', 'suggestion' ].includes( prop ) ),\n\t\t\tdata() {\n\t\t\t\treturn {\n\t\t\t\t\tsuggestionsList: suggestionsList.pages,\n\t\t\t\t\tactiveIndex: -1\n\t\t\t\t};\n\t\t\t},\n\t\t\tcomputed: {\n\t\t\t\tactionListeners() {\n\t\t\t\t\treturn makeActionListeners( args, argTypes );\n\t\t\t\t}\n\t\t\t},\n\t\t\tmethods: {\n\t\t\t\tonSuggestionMouseOver( index: number ) {\n\t\t\t\t\tthis.activeIndex = index;\n\t\t\t\t}\n\t\t\t},\n\t\t\ttemplate: `\n\t\t\t<ol class=\"sb-search__suggestions\">\n\t\t\t\t<li v-for=\"(suggestion, index) in suggestionsList\" >\n\t\t\t\t\t<wvui-typeahead-suggestion\n\t\t\t\t\t\tv-bind=\"$props\"\n\t\t\t\t\t\tv-on=\"actionListeners\"\n\t\t\t\t\t\t@mouseover=\"onSuggestionMouseOver( index )\"\n\t\t\t\t\t\t:active=\"activeIndex === index\"\n\t\t\t\t\t\t:suggestion=\"suggestion\"\n\t\t\t\t\t\t:key=\"suggestion.id\"\n\t\t\t\t\t/>\n\t\t\t\t</li>\n\t\t\t</ol>\n\t\t\t`\n\t\t} );\n\tstory.argTypes = {\n\t\tactive: {\n\t\t\ttable: {\n\t\t\t\tdisable: true\n\t\t\t}\n\t\t},\n\t\tquery: {\n\t\t\tdefaultValue: defaultQuery\n\t\t}\n\t};\n\treturn story;\n}\n\nexport const ExampleList = makeExampleListStory( defaultSuggestionsList, 'co' );\n\n// This story serves to demonstrate how the highlighting mechanism in the\n// typeahead-suggestion/typeahead-suggestion-title component handles languages with characters\n// represented by more than one Unicode scalar values.\n//\n// See https://phabricator.wikimedia.org/T277256 and https://phabricator.wikimedia.org/T35242 for\n// detail.\nexport const ExampleListWithGraphemes = makeExampleListStory( T277256SuggestionsList, 'ইতাল' );\n\nexport const ExampleListWithOverlongWords = makeExampleListStory(\n\tT280982SuggestionsList, 'Donaudampfschiffahrtselektrizit'\n);\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/typeahead-suggestion/TypeaheadSuggestion.test.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"jest/no-try-expect","replacedBy":["no-conditional-expect"]},{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/typeahead-suggestion/TypeaheadSuggestion.vue","messages":[{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":37,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":37,"endColumn":37},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":38,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":38,"endColumn":70},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":39,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":39,"endColumn":41},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":40,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":40,"endColumn":67},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":41,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":42,"endColumn":68},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":43,"column":1,"nodeType":"ImportDeclaration","messageId":"forbidden","endLine":43,"endColumn":74},{"ruleId":"es/no-modules","severity":2,"message":"ES2015 modules are forbidden.","line":45,"column":1,"nodeType":"ExportDefaultDeclaration","messageId":"forbidden","endLine":118,"endColumn":5},{"ruleId":"vue/no-boolean-default","severity":2,"message":"Boolean prop should not set a default (Vue defaults it to false).","line":51,"column":4,"nodeType":"Property","endLine":51,"endColumn":18},{"ruleId":"vue/no-boolean-default","severity":2,"message":"Boolean prop should not set a default (Vue defaults it to false).","line":71,"column":4,"nodeType":"Property","endLine":71,"endColumn":17},{"ruleId":"vue/no-boolean-default","severity":2,"message":"Boolean prop should not set a default (Vue defaults it to false).","line":75,"column":4,"nodeType":"Property","endLine":75,"endColumn":17},{"ruleId":"vue/no-boolean-default","severity":2,"message":"Boolean prop should not set a default (Vue defaults it to false).","line":79,"column":4,"nodeType":"Property","endLine":79,"endColumn":17},{"ruleId":"es/no-optional-chaining","severity":2,"message":"ES2020 optional chaining is forbidden.","line":107,"column":45,"nodeType":"Punctuator","messageId":"forbidden","endLine":107,"endColumn":47}],"errorCount":12,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<a\n\t\tv-if=\"suggestion\"\n\t\t:href=\"suggestionWikiLink\"\n\t\t:class=\"rootClasses\"\n\t\tclass=\"wvui-typeahead-suggestion\"\n\t\t@mouseover=\"onMouseOver\"\n\t\t@click=\"onClick\"\n\t>\n\t\t<span\n\t\t\tv-if=\"showThumbnail && suggestion.thumbnail\"\n\t\t\t:style=\"{ backgroundImage: thumbnailBackgroundImage }\"\n\t\t\tclass=\"wvui-typeahead-suggestion__thumbnail\"\n\t\t/>\n\t\t<span\n\t\t\tv-else-if=\"showThumbnail\"\n\t\t\tclass=\"wvui-typeahead-suggestion__thumbnail-placeholder\"\n\t\t><wvui-icon\n\t\t\t:icon=\"defaultThumbnailIcon\"\n\t\t\tclass=\"wvui-typeahead-suggestion__thumbnail-icon\"\n\t\t/></span>\n\t\t<span class=\"wvui-typeahead-suggestion__text\">\n\t\t\t<wvui-typeahead-suggestion-title\n\t\t\t\t:query=\"query\"\n\t\t\t\t:title=\"suggestion.title\"\n\t\t\t\t:highlight-query=\"highlightQuery\"\n\t\t\t/>\n\t\t\t<span\n\t\t\t\tv-if=\"showDescription && suggestion.description\"\n\t\t\t\tclass=\"wvui-typeahead-suggestion__description\"\n\t\t\t>{{ suggestion.description }}</span>\n\t\t</span>\n\t</a>\n</template>\n\n<script lang=\"ts\">\nimport Vue, { PropType } from 'vue';\nimport { SearchResult } from '../typeahead-search/http/SearchClient';\nimport WvuiIcon from '../icon/Icon.vue';\nimport { wvuiIconImageLayoutFrameless } from '../../themes/icons';\nimport WvuiTypeaheadSuggestionTitle\n\tfrom '../typeahead-suggestion-title/TypeaheadSuggestionTitle.vue';\nimport { UrlGenerator, createDefaultUrlGenerator } from './UrlGenerator';\n\nexport default Vue.extend( {\n\tname: 'WvuiTypeaheadSuggestion',\n\tcomponents: { WvuiTypeaheadSuggestionTitle: WvuiTypeaheadSuggestionTitle, WvuiIcon: WvuiIcon },\n\tprops: {\n\t\tactive: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false\n\t\t},\n\t\tarticlePath: {\n\t\t\ttype: String,\n\t\t\tdefault: '/w/index.php'\n\t\t},\n\t\tquery: {\n\t\t\ttype: String as PropType<string>,\n\t\t\tdefault: ''\n\t\t},\n\t\tsuggestion: {\n\t\t\ttype: Object as PropType<SearchResult>,\n\t\t\trequired: true\n\t\t},\n\t\turlGenerator: {\n\t\t\ttype: Object as PropType<UrlGenerator>,\n\t\t\tdefault: createDefaultUrlGenerator\n\t\t},\n\t\tshowThumbnail: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true\n\t\t},\n\t\tshowDescription: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true\n\t\t},\n\t\thighlightQuery: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true\n\t\t}\n\t},\n\tdata: function () {\n\t\treturn {\n\t\t\tdefaultThumbnailIcon: wvuiIconImageLayoutFrameless\n\t\t};\n\t},\n\tcomputed: {\n\t\trootClasses: function (): Record<string, boolean> {\n\t\t\treturn {\n\t\t\t\t'wvui-typeahead-suggestion--active': this.active\n\t\t\t};\n\t\t},\n\t\t/**\n\t\t * Generates wikipedia link for a suggestion.\n\t\t *\n\t\t * @return {string}\n\t\t * */\n\t\tsuggestionWikiLink: function (): string {\n\t\t\treturn this.urlGenerator.generateUrl( this.suggestion, undefined, this.articlePath );\n\t\t},\n\t\t/**\n\t\t * Generates a proper value for background-image.\n\t\t *\n\t\t * @return {string}\n\t\t * */\n\t\tthumbnailBackgroundImage: function (): string {\n\t\t\treturn 'url(' + this.suggestion.thumbnail?.url + ')';\n\t\t}\n\t},\n\tmethods: {\n\t\tonMouseOver: function ( event: MouseEvent ) {\n\t\t\tthis.$emit( 'mouseover', event );\n\t\t},\n\t\tonClick: function ( event: MouseEvent ) {\n\t\t\tthis.$emit( 'click', event );\n\t\t}\n\t}\n} );\n</script>\n\n<style lang=\"less\">\n@import ( reference ) '@/themes/wikimedia-ui.less';\n\n.wvui-typeahead-suggestion {\n\t// stylelint-disable-next-line plugin/no-unsupported-browser-features\n\tdisplay: flex;\n\talign-items: center;\n\tpadding: @padding-vertical-typeahead-suggestion @padding-horizontal-typeahead-suggestion;\n\ttext-decoration: none;\n\t// stylelint-disable-next-line plugin/no-unsupported-browser-features\n\thyphens: auto;\n\t// Legacy `word-wrap`; IE 6-11, Edge 12+, Firefox 3.5+, Chrome 4+, Safari 3.1+,\n\t//   Opera 11.5+, iOS 3.2+, Android 2.1+\n\t// `overflow-wrap` is W3 standard, but it doesn't seem as if browser vendors\n\t//   will abandon `word-wrap` (it has wider support), therefore no duplication.\n\tword-wrap: break-word;\n\n\t// &--active is supposed to be used both when hover\n\t// and when navigating with keyboard.\n\t&--active {\n\t\tbackground-color: @background-color-base--hover;\n\t}\n\n\t&__thumbnail-placeholder,\n\t&__thumbnail {\n\t\tbackground-position: center;\n\t\tbackground-repeat: no-repeat;\n\t\t// stylelint-disable-next-line plugin/no-unsupported-browser-features\n\t\tbackground-size: cover;\n\t\t// Prevent thumbnail width from shrinking when descriptions are long.\n\t\tflex-shrink: 0;\n\t\twidth: @size-search-figure;\n\t\theight: @size-search-figure;\n\t\tmargin-right: @margin-end-typeahead-suggestion-thumb;\n\t\tborder-radius: @border-radius-base;\n\t\t// Borders tend to cut into the border-radius and it makes the\n\t\t// border-radius look smaller on the inside of the box than the outside.\n\t\t// Using a box-shadow disguised as a border prevents that from happening.\n\t\tbox-shadow: 0 0\n\t\t\t@border-width-base\n\t\t\t@border-width-base\n\t\t\t@border-color-typeahead-suggestion-thumb;\n\t}\n\n\t&__thumbnail {\n\t\tdisplay: inline-block;\n\n\t\t&-placeholder {\n\t\t\tbackground-color: @background-color-typeahead-suggestion-placeholder;\n\t\t\t// stylelint-disable-next-line plugin/no-unsupported-browser-features\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t}\n\n\t\t&-icon {\n\t\t\tcolor: @color-accessory;\n\t\t}\n\t}\n\n\t&__text {\n\t\toverflow: hidden;\n\n\t\t.wvui-typeahead-suggestion__description {\n\t\t\tcolor: @color-placeholder;\n\t\t\tdisplay: block;\n\t\t\toverflow: hidden;\n\t\t\tfont-size: @font-size-typeahead-suggestion-description;\n\t\t\ttext-overflow: ellipsis;\n\t\t\twhite-space: nowrap;\n\t\t}\n\t}\n}\n</style>\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/components/typeahead-suggestion/UrlGenerator.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/composables/useModelWrapper.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/entries/wvui-icons.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/entries/wvui-search.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/entries/wvui.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/http/fetch.test.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"jest/no-try-expect","replacedBy":["no-conditional-expect"]},{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/http/fetch.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/themes/icons.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/types/Environment.d.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/types/VueLoader.d.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/utils/StoryUtils.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/utils/StringUtils.test.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"jest/no-try-expect","replacedBy":["no-conditional-expect"]},{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/src/utils/StringUtils.ts","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]}]

$ npm ci
npm WARN prepare removing existing node_modules/ before installation

> ejs@2.7.4 postinstall /src/repo/node_modules/webpack-bundle-analyzer/node_modules/ejs
> node ./postinstall.js

Thank you for installing EJS: built with the Jake JavaScript build tool (https://jakejs.com/)


> fsevents@1.2.13 install /src/repo/node_modules/watchpack-chokidar2/node_modules/fsevents
> node install.js


Skipping 'fsevents' build as platform linux is not supported

> core-js@2.6.12 postinstall /src/repo/node_modules/babel-runtime/node_modules/core-js
> node -e "try{require('./postinstall')}catch(e){}"


> deasync@0.1.21 install /src/repo/node_modules/deasync
> node ./build.js

`linux-x64-node-10` exists; testing
Binary is fine; exiting

> spawn-sync@1.0.15 postinstall /src/repo/node_modules/spawn-sync
> node postinstall


> pre-commit@1.2.2 install /src/repo/node_modules/pre-commit
> node install.js

pre-commit:
pre-commit: Detected an existing git pre-commit hook
pre-commit: Old pre-commit hook backuped to pre-commit.old
pre-commit:

> core-js@3.11.3 postinstall /src/repo/node_modules/core-js
> node -e "try{require('./postinstall')}catch(e){}"


> iltorb@2.4.5 install /src/repo/node_modules/iltorb
> node ./scripts/install.js || node-gyp rebuild

info looking for cached prebuild @ /cache/_cacache/_prebuilds/2a34dd-iltorb-v2.4.5-node-v64-linux-x64.tar.gz
info found cached prebuild 
info unpacking @ /cache/_cacache/_prebuilds/2a34dd-iltorb-v2.4.5-node-v64-linux-x64.tar.gz
info unpack resolved to /src/repo/node_modules/iltorb/build/bindings/iltorb.node
info unpack required /src/repo/node_modules/iltorb/build/bindings/iltorb.node successfully
info install Successfully installed iltorb binary!

> core-js@2.6.12 postinstall /src/repo/node_modules/babel-register/node_modules/core-js
> node -e "try{require('./postinstall')}catch(e){}"


> core-js-pure@3.11.3 postinstall /src/repo/node_modules/core-js-pure
> node -e "try{require('./postinstall')}catch(e){}"


> @wikimedia/wvui@0.2.0 prepare /src/repo
> husky install

husky - Git hooks installed
added 2834 packages in 33.377s

$ npm test

> @wikimedia/wvui@0.2.0 test /src/repo
> npm -s run build && npm -s run test:all && npm -s run doc:all

Error parsing bundle asset "/src/repo/dist/commonjs2/wvui-search.commonjs2.js": no such file

No bundles were parsed. Analyzer will show only original module sizes from stats file.

Error parsing bundle asset "/src/repo/dist/commonjs2/wvui.commonjs2.js": no such file

No bundles were parsed. Analyzer will show only original module sizes from stats file.

Error parsing bundle asset "/src/repo/dist/wvui.js": no such file

No bundles were parsed. Analyzer will show only original module sizes from stats file.

Child wvui:
    Built at: 06/11/2021 3:35:01 AM
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(278,10)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(282,6)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(286,6)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(291,5)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(293,5)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(293,20)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(293,53)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(295,5)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(295,43)
          TS7031: Binding element 'results' implicitly has an 'any' type.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(296,6)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(304,6)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(305,13)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(305,13)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
Child wvui-icons:
    Built at: 06/11/2021 3:34:51 AM
Child wvui.commonjs2:
    Built at: 06/11/2021 3:34:56 AM
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(278,10)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(282,6)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(286,6)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(291,5)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(293,5)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(293,20)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(293,53)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(295,5)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(295,43)
          TS7031: Binding element 'results' implicitly has an 'any' type.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(296,6)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(304,6)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(305,13)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(305,13)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
Child wvui-icons.commonjs2:
    Built at: 06/11/2021 3:34:49 AM
Child wvui-search.commonjs2:
    Built at: 06/11/2021 3:34:54 AM
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(278,10)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(282,6)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(286,6)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(291,5)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(293,5)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(293,20)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(293,53)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(295,5)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(295,43)
          TS7031: Binding element 'results' implicitly has an 'any' type.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(296,6)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(304,6)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(305,13)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
    
    ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts
    [tsl] ERROR in /src/repo/src/components/typeahead-search/TypeaheadSearch.vue.ts(305,13)
          TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
npm ERR! Test failed.  See above for more details.

Traceback (most recent call last):
  File "/venv/lib/python3.7/site-packages/runner-0.1.0-py3.7.egg/runner/__init__.py", line 1318, in main
    libup.run(args.repo, args.output, args.branch)
  File "/venv/lib/python3.7/site-packages/runner-0.1.0-py3.7.egg/runner/__init__.py", line 1257, in run
    self.npm_upgrade(plan)
  File "/venv/lib/python3.7/site-packages/runner-0.1.0-py3.7.egg/runner/__init__.py", line 965, in npm_upgrade
    self.npm_test()
  File "/venv/lib/python3.7/site-packages/runner-0.1.0-py3.7.egg/runner/__init__.py", line 255, in npm_test
    self.check_call(['npm', 'test'])
  File "/venv/lib/python3.7/site-packages/runner-0.1.0-py3.7.egg/runner/shell2.py", line 40, in check_call
    res.check_returncode()
  File "/usr/lib/python3.7/subprocess.py", line 428, in check_returncode
    self.stderr)
subprocess.CalledProcessError: Command '['npm', 'test']' returned non-zero exit status 1.

npm dependencies

Development dependencies

Logs

Source code is licensed under the AGPL.