diff --git a/Dockerfile b/Dockerfile index 457470cfc..9bb55137c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,2 +1,2 @@ -FROM nginx:stable-alpine -COPY ./storybook-static /usr/share/nginx/html \ No newline at end of file +FROM nginxinc/nginx-unprivileged:1.23-alpine +COPY ./storybook-static /usr/share/nginx/html diff --git a/Jenkinsfile b/Jenkinsfile index c8e045b50..9b5e8cea4 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,6 +1,13 @@ @Library('jenkins-library' ) _ def pipeline = new org.js.LibPipeline(steps: this, - dockerImageName: 'soramitsu/soramitsu-js-ui-library', - libExamplesBuildCmds: ['yarn storybook:build']) + buildDockerImage: 'build-tools/node:16-ubuntu', + dockerImageName: 'soramitsu/soramitsu-js-ui-library', + libExamplesBuildCmds: ['yarn storybook:build'], + sonarProjectName: 'soramitsu-js-ui-library', + sonarProjectKey: 'jp.co.soramitsu:soramitsu-js-ui-library', + sonarSrcPath: 'src', + sonarTestsPath: 'tests', + dojoProductType: 'Dev' +) pipeline.runPipeline() diff --git a/build/rollup.config.js b/build/rollup.config.js index 94c6f9489..06288e6fb 100644 --- a/build/rollup.config.js +++ b/build/rollup.config.js @@ -3,9 +3,9 @@ import typescript from 'rollup-plugin-typescript2' import pkg from '../package.json' import scss from 'rollup-plugin-scss' import resolve from '@rollup/plugin-node-resolve' -import commonjs from 'rollup-plugin-commonjs' +import commonjs from '@rollup/plugin-commonjs' import copy from 'rollup-plugin-copy' -import { terser } from 'rollup-plugin-terser' +import terser from '@rollup/plugin-terser' import del from 'rollup-plugin-delete' import multiInput from 'rollup-plugin-multi-input' @@ -22,6 +22,7 @@ export default { 'src/types/Locale.ts', 'src/types/Theme.ts', 'src/types/components.ts', + 'src/types/directives.ts', 'src/types/index.ts', 'src/locale/index.ts', 'src/plugins/*.ts', @@ -80,7 +81,7 @@ export default { }), scss(), resolve(), - /* eslint-disable @typescript-eslint/camelcase */ + /* eslint-disable camelcase */ terser({ keep_classnames: true }), del({ targets: [ diff --git a/config/storybook/main.js b/config/storybook/main.js index d2eb1cb18..78ba3ceb6 100644 --- a/config/storybook/main.js +++ b/config/storybook/main.js @@ -1,25 +1,11 @@ module.exports = { - stories: ['../../src/**/*.stories.(js|jsx|ts|tsx|mdx)'], + core: { + builder: '@storybook/builder-webpack5' + }, + stories: ['../../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'], addons: [ - '@storybook/addon-actions', - '@storybook/addon-a11y', - { - name: '@storybook/addon-docs', - options: { - babelOptions: { - presets: [ - [ - '@vue/cli-plugin-babel/preset', - { - jsx: false - } - ] - ] - } - } - }, '@storybook/addon-essentials', - '@storybook/addon-knobs', + '@storybook/addon-a11y', '@storybook/addon-storysource' ], webpackFinal: (config) => { diff --git a/config/storybook/neu-theme-variables.scss b/config/storybook/neu-theme-variables.scss index b06470e36..e1aed7f51 100644 --- a/config/storybook/neu-theme-variables.scss +++ b/config/storybook/neu-theme-variables.scss @@ -24,17 +24,20 @@ $s-color-base-background-hover: #F7F3F4; $s-color-base-disabled: #FDF7FB; $s-color-base-on-disabled: #A19A9D; $s-color-base-on-accent: #FFFFFF; +$s-color-outline: rgba(0, 0, 0, 0.5); // Utility colors $s-color-utility-body: #F7F3F4; $s-color-utility-surface: #FDF7FB; $s-color-utility-overlay: rgba(42, 23, 31, 0.1); // Status colors -$s-color-status-success: #34AD87; -$s-color-status-warning: #479AEF; -$s-color-status-error: #F754A3; -$s-color-status-success-background: #B9EBDB; -$s-color-status-warning-background: #C6E2FF; -$s-color-status-error-background: #FFD8EB; +$s-color-status-success: #34AD87 !default; +$s-color-status-warning: #EBA332 !default; +$s-color-status-error: #F754A3 !default; +$s-color-status-info: #479AEF !default; +$s-color-status-success-background: #B9EBDB !default; +$s-color-status-warning-background: #FCEEBD !default; +$s-color-status-error-background: #FFD8EB !default; +$s-color-status-info-background: #C6E2FF !default; // Shadows $s-shadow-surface: 1px 1px 5px var(--s-shadow-color-dark), inset 1px 1px 1px var(--s-shadow-color-dark); // container // Size @@ -63,15 +66,18 @@ $s-size-mini: 24px; --s-color-base-disabled: #{$s-color-base-disabled}; --s-color-base-on-disabled: #{$s-color-base-on-disabled}; --s-color-base-on-accent: #{$s-color-base-on-accent}; + --s-color-outline: #{$s-color-outline}; --s-color-utility-body: #{$s-color-utility-body}; --s-color-utility-surface: #{$s-color-utility-surface}; --s-color-utility-overlay: #{$s-color-utility-overlay}; --s-color-status-success: #{$s-color-status-success}; --s-color-status-warning: #{$s-color-status-warning}; --s-color-status-error: #{$s-color-status-error}; + --s-color-status-info: #{$s-color-status-info}; --s-color-status-success-background: #{$s-color-status-success-background}; --s-color-status-warning-background: #{$s-color-status-warning-background}; --s-color-status-error-background: #{$s-color-status-error-background}; + --s-color-status-info-background: #{$s-color-status-info-background}; // Shadows --s-shadow-surface: #{$s-shadow-surface}; } diff --git a/config/storybook/preview.ts b/config/storybook/preview.ts index 93836b3ee..b30b1095a 100644 --- a/config/storybook/preview.ts +++ b/config/storybook/preview.ts @@ -1,7 +1,5 @@ import Vue from 'vue' -import { addParameters, addDecorator } from '@storybook/vue' -import { withA11y } from '@storybook/addon-a11y' -import { DocsPage } from '@storybook/addon-docs/blocks' +import { DocsPage } from '@storybook/addon-docs' import ElColorPicker from 'element-ui/lib/color-picker' import '../../src/styles/index.scss' @@ -21,7 +19,7 @@ setTheme() document.documentElement.style.setProperty('color', 'var(--s-color-base-content-primary)') document.documentElement.style.setProperty('background-color', 'var(--s-color-utility-body)') -addParameters({ +export const parameters = { options: { showRoots: true }, @@ -34,45 +32,50 @@ addParameters({ // completely hide a dependency/dependents block if it has no elements // by default this is false hideEmpty: true + }, + a11y: { + element: '#storybook-root', + config: {}, + options: {}, + manual: true } -}) +} -addDecorator(withA11y) -const neuLabelCode = '%F0%9F%9F%A3' -addDecorator(() => ({ - components: { SDesignSystemProvider, SButton, SCheckbox }, - template: ` -
- - -
-
- -
-
`, - store: mainStore, - computed: { - // hasNeumorphicMode: () => window.location.href.includes(neuLabelCode) || window.location.href.includes('🟣'), // Set v-if="hasNeumorphicMode" if needed - theme: () => mainStore?.getters?.libraryTheme as Theme, - designSystem: () => mainStore?.getters?.libraryDesignSystem as DesignSystem - }, - methods: { - handleThemeChange: () => { - switchTheme() +export const decorators = [ + () => ({ + components: { SDesignSystemProvider, SButton, SCheckbox }, + template: ` +
+ + +
+
+ +
+
`, + store: mainStore, + computed: { + theme: () => mainStore?.getters?.libraryTheme as Theme, + designSystem: () => mainStore?.getters?.libraryDesignSystem as DesignSystem }, - handleDesignSystemChange: (designSystem: DesignSystem) => { - const newDesignSystem = designSystem === DesignSystem.DEFAULT ? DesignSystem.NEUMORPHIC : DesignSystem.DEFAULT - setDesignSystem(newDesignSystem) + methods: { + handleThemeChange: () => { + switchTheme() + }, + handleDesignSystemChange: (designSystem: DesignSystem) => { + const newDesignSystem = designSystem === DesignSystem.DEFAULT ? DesignSystem.NEUMORPHIC : DesignSystem.DEFAULT + setDesignSystem(newDesignSystem) + } } - } -})) + }) +] diff --git a/jest.config.js b/jest.config.js index 8b359f812..64836c2c1 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,3 +1,7 @@ module.exports = { - preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel' + preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel', + collectCoverage: true, + collectCoverageFrom: ['src/**/*.{ts,tsx}'], + coverageReporters: ['lcov'], + coveragePathIgnorePatterns: ['node_modules/', 'coverage/'], } diff --git a/package.json b/package.json index cf2e363ae..bb28b13d8 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { - "name": "@soramitsu/soramitsu-js-ui", - "version": "1.0.27", + "name": "@soramitsu-ui/ui-vue2", + "version": "1.1.3", "private": false, "publishConfig": { - "registry": "https://nexus.iroha.tech/repository/npm-soramitsu/" + "access": "public" }, "license": "Apache-2.0", "licenses": [ @@ -19,74 +19,74 @@ "typings": "./lib/index.d.ts", "scripts": { "build": "rm -rf lib && rollup -c ./build/rollup.config.js", - "test:unit": "vue-cli-service test:unit", - "test:all": "yarn test:unit", + "test:unit": "vue-cli-service test:unit --coverage", + "test:all": "yarn test:unit --coverage", "lint": "vue-cli-service lint", "storybook:build": "vue-cli-service storybook:build -c config/storybook", "storybook:serve": "vue-cli-service storybook:serve -p 6006 -c config/storybook" }, "dependencies": { - "core-js": "^3.6.4", - "element-resize-detector": "^1.2.1", - "element-ui": "^2.15.5", - "lodash": "^4.17.15", + "core-js": "^3.26.1", + "element-resize-detector": "^1.2.4", + "element-ui": "^2.15.12", + "lodash": "^4.17.21", "throttle-debounce": "^1.0.1", - "v-jsoneditor": "^1.4.1", - "vue": "^2.6.11", - "vue-class-component": "^7.2.3", - "vue-i18n": "^8.11.2", + "v-jsoneditor": "^1.4.5", + "vue": "^2.6.14", + "vue-class-component": "^7.2.6", + "vue-i18n": "^8.28.2", "vue-property-decorator": "^9.1.2", - "vuex": "^3.1.3", - "vuex-class": "^0.3.2" + "vuex": "^3.6.2" }, "devDependencies": { - "@rollup/plugin-node-resolve": "^6.0.0", - "@storybook/addon-a11y": "^5.3.19", - "@storybook/addon-actions": ">=5.3.0", - "@storybook/addon-docs": ">=5.3.0", - "@storybook/addon-essentials": "^5.3.19", - "@storybook/addon-knobs": "^5.3.19", - "@storybook/addon-storysource": "^5.3.19", - "@storybook/theming": "^5.3.19", - "@storybook/vue": ">=5.3.0", - "@types/jest": "^25.2.3", - "@types/lodash": "^4.14.155", - "@typescript-eslint/eslint-plugin": "^2.26.0", - "@typescript-eslint/parser": "^2.26.0", - "@vue/cli-plugin-babel": "~4.3.0", - "@vue/cli-plugin-eslint": "~4.3.0", - "@vue/cli-plugin-typescript": "~4.3.0", - "@vue/cli-plugin-unit-jest": "~4.3.0", - "@vue/cli-service": "~4.3.0", + "@rollup/plugin-commonjs": "^23.0.3", + "@rollup/plugin-node-resolve": "^15.0.1", + "@rollup/plugin-terser": "^0.1.0", + "@storybook/addon-a11y": "6.5.16", + "@storybook/addon-essentials": "6.5.16", + "@storybook/addon-storysource": "6.5.16", + "@storybook/builder-webpack5": "6.5.16", + "@storybook/core-server": "6.5.16", + "@storybook/manager-webpack5": "6.5.16", + "@storybook/theming": "6.5.16", + "@storybook/vue": "6.5.16", + "@types/jest": "^29.2.3", + "@types/lodash": "^4.14.190", + "@typescript-eslint/eslint-plugin": "^5.45.0", + "@typescript-eslint/parser": "^5.45.0", + "@vue/cli-plugin-babel": "5.0.8", + "@vue/cli-plugin-eslint": "5.0.8", + "@vue/cli-plugin-typescript": "5.0.8", + "@vue/cli-plugin-unit-jest": "5.0.8", + "@vue/cli-service": "5.0.8", "@vue/eslint-config-standard": "^5.1.2", "@vue/eslint-config-typescript": "^5.0.2", - "@vue/test-utils": "1.0.0-beta.31", - "eslint": "^6.7.2", - "eslint-plugin-import": "^2.20.2", + "@vue/test-utils": "^1.2.2", + "@vue/vue2-jest": "27.0.0-alpha.2", + "eslint": "^7.29.0", + "eslint-plugin-import": "^2.26.0", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^4.2.1", - "eslint-plugin-standard": "^4.0.0", - "eslint-plugin-vue": "^6.2.2", - "jest": "^26.0.1", + "eslint-plugin-promise": "^5.2.0", + "eslint-plugin-standard": "^5.0.0", + "eslint-plugin-vue": "^7.20.0", + "jest": "^27.2.2", "lint-staged": "^9.5.0", - "node-sass": "^4.12.0", - "postcss": "^8.2.10", - "rollup": "^1.27.8", - "rollup-plugin-commonjs": "^10.1.0", + "postcss": "^8.2.13", + "rollup": "2.78.0", "rollup-plugin-copy": "^3.4.0", "rollup-plugin-delete": "^1.1.0", "rollup-plugin-multi-input": "^1.3.1", - "rollup-plugin-scss": "^2.5.0", - "rollup-plugin-terser": "^5.1.2", + "rollup-plugin-scss": "^3.0.0", "rollup-plugin-typescript2": "^0.25.2", "rollup-plugin-vue": "^5.1.4", - "sass-loader": "^8.0.2", + "sass": "^1.56.1", + "sass-loader": "^13.2.0", "storybook-vue-router": "^1.0.7", - "ts-jest": "^26.0.0", - "typescript": "~3.8.3", + "ts-jest": "^27.0.5", + "typescript": "~4.4.4", "vue-cli-plugin-storybook": "~1.2.2", "vue-router": "^3.3.4", - "vue-template-compiler": "^2.6.11" + "vue-template-compiler": "^2.6.14" }, "postcss": { "plugins": { @@ -99,8 +99,8 @@ "*.js" ], "resolutions": { - "react": "16.13.0", - "react-dom": "16.13.0" + "react": "16.14.0", + "react-dom": "16.14.0" }, "sideEffects": false, "gitHooks": { diff --git a/src/components/Button/SButton/SButton.vue b/src/components/Button/SButton/SButton.vue index 03a03e06e..fbd53673d 100644 --- a/src/components/Button/SButton/SButton.vue +++ b/src/components/Button/SButton/SButton.vue @@ -103,7 +103,7 @@ export default class SButton extends Mixins(SizeMixin, BorderRadiusMixin, Design /** * Button tabindex */ - @Prop({ default: '0', type: String }) readonly tabindex!: string + @Prop({ default: 0, type: [Number, String] }) readonly tabindex!: number | string /** * Placement of the tooltip. You can use any value from the `TooltipPlacement` enum. * @@ -111,8 +111,9 @@ export default class SButton extends Mixins(SizeMixin, BorderRadiusMixin, Design */ @Prop({ default: TooltipPlacement.TOP, type: String }) readonly tooltipPlacement!: PopoverPlacement - @Inject({ default: '', from: 'elForm' }) elForm!: ElForm - @Inject({ default: '', from: 'elFormItem' }) elFormItem!: ElFormItem + @Inject({ default: '', from: 'elForm' }) private elForm!: ElForm + @Inject({ default: '', from: 'elFormItem' }) private elFormItem!: ElFormItem + @Inject({ default: '', from: 'sButtonGroup' }) private sButtonGroup!: any private iconLeftOffset = 0 elementIcon = '' @@ -130,12 +131,16 @@ export default class SButton extends Mixins(SizeMixin, BorderRadiusMixin, Design if (this.designSystemClass) { cssClasses.push(this.designSystemClass) } - if ((this.elForm || this.elFormItem || {}).size) { - cssClasses.push(`s-${(this.elForm || this.elFormItem).size}`) + const externalSize = (this.elForm || this.elFormItem || this.sButtonGroup || {}).size + if (externalSize) { + cssClasses.push(`s-${externalSize}`) } else if (this.isStandardSize) { cssClasses.push(`s-${this.size}`) } - if (this.isStandardBorderRadius) { + const externalRadius = (this.sButtonGroup || {}).borderRadius + if (externalRadius) { + cssClasses.push(`s-border-radius-${externalRadius}`) + } else if (this.isStandardBorderRadius) { cssClasses.push(`s-border-radius-${this.borderRadius}`) } if ((Object.values(ButtonTypes) as Array).includes(this.type)) { @@ -202,7 +207,7 @@ export default class SButton extends Mixins(SizeMixin, BorderRadiusMixin, Design } mounted (): void { - this.$el.setAttribute('tabindex', this.tabindex) + this.$el.setAttribute('tabindex', this.tabindex.toString()) this.$watch('loading', (value) => { if (!value) { return diff --git a/src/components/Button/SButtonGroup/SButtonGroup.vue b/src/components/Button/SButtonGroup/SButtonGroup.vue index 8216ab292..216282f5d 100644 --- a/src/components/Button/SButtonGroup/SButtonGroup.vue +++ b/src/components/Button/SButtonGroup/SButtonGroup.vue @@ -1,17 +1,36 @@ diff --git a/src/components/Checkbox/SCheckbox.vue b/src/components/Checkbox/SCheckbox.vue index 0b06ff38f..a7b4564cf 100644 --- a/src/components/Checkbox/SCheckbox.vue +++ b/src/components/Checkbox/SCheckbox.vue @@ -1,7 +1,7 @@ diff --git a/src/components/Radio/SRadioGroup/SRadioGroup.vue b/src/components/Radio/SRadioGroup/SRadioGroup.vue index 5dfab5cc7..31dbc7c74 100644 --- a/src/components/Radio/SRadioGroup/SRadioGroup.vue +++ b/src/components/Radio/SRadioGroup/SRadioGroup.vue @@ -9,7 +9,7 @@ diff --git a/src/components/ScrollSections/SScrollSections/SScrollSections.vue b/src/components/ScrollSections/SScrollSections/SScrollSections.vue index 1cafe9a9b..2c661be95 100644 --- a/src/components/ScrollSections/SScrollSections/SScrollSections.vue +++ b/src/components/ScrollSections/SScrollSections/SScrollSections.vue @@ -132,7 +132,7 @@ export default class SScrollSections extends Vue { } get scrollableParent (): any { - return this.parent ? this.$parent.$el : window + return this.parent && this.$parent ? this.$parent.$el : window } private setState (): void { diff --git a/src/components/Select/SSelect/SSelect.vue b/src/components/Select/SSelect/SSelect.vue index 4bcc7c8e3..4475905f3 100644 --- a/src/components/Select/SSelect/SSelect.vue +++ b/src/components/Select/SSelect/SSelect.vue @@ -18,6 +18,7 @@ @focus="handleFocus" @visible-change="handleVisibleChange" @clear="handleClear" + @change="handleChange" :filterable="filterable" > @@ -35,17 +36,22 @@ import { ElFormItem } from 'element-ui/types/form-item' import SizeMixin from '../../../mixins/SizeMixin' import BorderRadiusMixin from '../../../mixins/BorderRadiusMixin' +import DesignSystemInject from '../../DesignSystem/DesignSystemInject' import { Autocomplete } from '../../Input/consts' import { InputTypes } from '../consts' +type SingleSelectValue = boolean | string | number; +type MultipleSelectValue = Array; +type SelectValue = SingleSelectValue | MultipleSelectValue; + @Component({ components: { ElSelect } }) -export default class SSelect extends Mixins(SizeMixin, BorderRadiusMixin) { +export default class SSelect extends Mixins(SizeMixin, BorderRadiusMixin, DesignSystemInject) { /** * Selected value. Can be used with `v-model` */ - @Prop() readonly value!: any + @Prop({ type: [Boolean, String, Number, Array] }) readonly value!: SelectValue /** * Input type of the select component. Available values: `"input"`, `"select"`. * @@ -125,12 +131,26 @@ export default class SSelect extends Mixins(SizeMixin, BorderRadiusMixin) { */ @Prop({ type: Boolean, default: false }) readonly filterable!: boolean + get model (): SelectValue { + return this.value + } + + set model (value: SelectValue) { + if (JSON.stringify(value) !== JSON.stringify(this.value)) { + this.$emit('input', value) + } + } + @Ref('select') select!: any @Inject({ default: '', from: 'elForm' }) elForm!: ElForm @Inject({ default: '', from: 'elFormItem' }) elFormItem!: ElFormItem - model = this.value + @Watch('model') + private handleValueChange (): void { + this.updateInputValue() + } + focused = false private updateInputValue (): void { @@ -142,19 +162,7 @@ export default class SSelect extends Mixins(SizeMixin, BorderRadiusMixin) { tags.remove() } const input = this.select.$el.getElementsByClassName('el-input__inner')[0] as HTMLInputElement - input.value = this.model.length ? `${this.multipleTextPrefix || this.placeholder} (${this.model.length})` : '' - } - - @Watch('value') - private handlePropChange (value: any): void { - this.model = value - } - - @Watch('model') - private handleValueChange (value: any): void { - this.$emit('input', value) - this.$emit('change', value) - this.updateInputValue() + input.value = Array.isArray(this.model) && (this.model as MultipleSelectValue).length ? `${this.multipleTextPrefix || this.placeholder} (${this.model.length})` : '' } mounted (): void { @@ -163,6 +171,9 @@ export default class SSelect extends Mixins(SizeMixin, BorderRadiusMixin) { get computedPopperClass (): string { const cssClasses: Array = [] + if (this.designSystemClass) { + cssClasses.push(this.designSystemClass) + } if (this.popperClass) { cssClasses.push(this.popperClass) } @@ -174,6 +185,9 @@ export default class SSelect extends Mixins(SizeMixin, BorderRadiusMixin) { get computedClasses (): Array { const cssClasses: Array = [] + if (this.designSystemClass) { + cssClasses.push(this.designSystemClass) + } if (this.inputType === 'select') { if ((this.elForm || this.elFormItem || {}).size) { cssClasses.push(`s-${(this.elForm || this.elFormItem).size}`) @@ -193,7 +207,7 @@ export default class SSelect extends Mixins(SizeMixin, BorderRadiusMixin) { if (this.disabled) { cssClasses.push('s-disabled') } - if ((!this.multiple && this.model) || (this.multiple && this.model.length !== 0)) { + if ((!this.multiple && this.model) || (this.multiple && Array.isArray(this.model) && this.model.length !== 0)) { cssClasses.push('s-has-value') } return cssClasses @@ -233,6 +247,10 @@ export default class SSelect extends Mixins(SizeMixin, BorderRadiusMixin) { this.$emit('clear') } + handleChange (value: SelectValue): void { + this.$emit('change', value) + } + public focus (): void { this.select.focus() } diff --git a/src/components/Slider/SSlider.vue b/src/components/Slider/SSlider.vue index 881f93473..0b9573952 100644 --- a/src/components/Slider/SSlider.vue +++ b/src/components/Slider/SSlider.vue @@ -19,14 +19,13 @@ :range="range" :marks="marks" :disabled="disabled" - @input="handleInput" @change="handleChange" />