diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md index ac72b99ec..b438c6424 100644 --- a/packages/ui/CHANGELOG.md +++ b/packages/ui/CHANGELOG.md @@ -1,5 +1,23 @@ # @soramitsu-ui/ui +## 0.13.13 + +### Patch Changes + +- b5029a5b: **feat**(`SSelectDropdown`): add mandatory prop + +## 0.13.12 + +### Patch Changes + +- 0a615d5c: **feat**(`STextField`): add validations list prop + +## 0.13.11 + +### Patch Changes + +- 8d0d2d66: **fix**(`SDropdown`): add expression to display dropdown options correctly + ## 0.13.10 ### Patch Changes diff --git a/packages/ui/cypress/component/STextField.spec.cy.ts b/packages/ui/cypress/component/STextField.spec.cy.ts index 08bfff226..f686cfe2f 100644 --- a/packages/ui/cypress/component/STextField.spec.cy.ts +++ b/packages/ui/cypress/component/STextField.spec.cy.ts @@ -347,3 +347,67 @@ describe('Passing extra attributes', () => { findInput().should('have.attr', 'data-count', 1) }) }) + +it('Validations list works', () => { + cy.mount({ + setup() { + const model = ref('') + + // since we don't change model value by using type() method we have to edit it manually + // because message slot relies on model.value correctness otherwise it's always empty + setTimeout(() => { + // matches some rules + model.value = 'a1' + }, 1000) + + setTimeout(() => { + // matches every rule + model.value = 'a1!' + }, 2000) + + function validations(value: string) { + return [ + { + rule: /[a-z]/.test(value), + message: 'At least 1 lowercase letter', + }, + { + rule: /\d/.test(value), + message: 'At least 1 digit', + }, + { + rule: /[!"#$%&'()*+,./:;<=>?@[\]^_`{|}~\\-]/.test(value), + message: 'At least 1 special character', + }, + ] + } + + const validationsList = computed(() => { + return { + title: 'String must contain:', + validations: validations(model.value).map((v) => ({ isMatching: v.rule, ...v })), + showOnFocusOnly: true, + } + }) + + return { + validationsList, + model, + } + }, + template: ``, + }) + + findMsg().should('not.exist') + findInput().focus() + findMsg().should('exist') + // first timeout + findInput().should('have.value', 'a1') + findInput().blur() + findMsg().should('not.exist') + findInput().focus() + findMsg().should('exist') + // second timeout + findInput().should('have.value', 'a1!') + findMsg().should('not.exist') +}) diff --git a/packages/ui/cypress/component/Select.spec.cy.ts b/packages/ui/cypress/component/Select.spec.cy.ts index 25af419c9..7eba67a60 100644 --- a/packages/ui/cypress/component/Select.spec.cy.ts +++ b/packages/ui/cypress/component/Select.spec.cy.ts @@ -244,6 +244,127 @@ it('SSelect - clicking options, checking auto-transformations', () => { assertValue('2') }) +it('SSelect simple options mandatory mode works', () => { + cy.mount({ + setup() { + const model = ref(null) + + const options = [ + { label: 'Opt 1', value: 1 }, + { label: 'Opt 2', value: 2 }, + { label: 'Opt 3', value: 3 }, + ] + + const [multiple, toggleMultiple] = useToggle(false) + + return { model, options, multiple, toggleMultiple } + }, + template: ` +
Value: {{ model }}
+ + + `, + }) + + function assertValue(val: string) { + cy.contains(`Value: ${val}`) + } + + cy.contains('Dap').click() + cy.get('.s-select-option__content').eq(0).click() + assertValue('1') + + cy.contains('Dap').click() + cy.get('.s-select-option__content').eq(0).click() + assertValue('1') + + cy.get('button').contains('multiple').click() + cy.contains('Dap').click() + cy.get('.s-select-option__content').eq(1).click() + assertValue('[ 1, 2 ]') + + cy.get('.s-select-option__content').eq(1).click() + cy.get('.s-select-option__content').eq(0).click() + assertValue('[ 1 ]') +}) + +it('SSelect group options mandatory mode works', () => { + cy.mount({ + setup() { + const model = ref(null) + + const options = [ + { + header: '1st group', + selectAllBtn: true, + items: [ + { + label: 'Germany', + value: 'du', + }, + { + label: 'England', + value: 'en', + }, + { + label: 'United Arab Emirates', + value: 'ae', + }, + ], + }, + { + header: '2nd group', + selectAllBtn: false, + items: [ + { + label: 'Iceland', + value: 'is', + }, + { + label: 'Japan', + value: 'jp', + }, + ], + }, + ] + + return { model, options } + }, + template: ` +
Value: {{ model }}
+ + `, + }) + + function assertValue(val: string) { + cy.contains(`Value: ${val}`) + } + + cy.contains('Dap').click() + + cy.get('.s-select-dropdown__action').click() + assertValue(`[ "du", "en", "ae" ]`) + cy.get('.s-select-dropdown__action').click() + assertValue(`[ "du", "en", "ae" ]`) + cy.get('.s-select-option__content').eq(3).click() + assertValue(`[ "du", "en", "ae", "is" ]`) + cy.get('.s-select-dropdown__action').click() + assertValue(`[ "is" ]`) + cy.get('.s-select-option__content').eq(3).click() + assertValue(`[ "is" ]`) +}) + it('SDropdown - model usage works', () => { cy.mount({ setup() { diff --git a/packages/ui/etc/api/ui.api.md b/packages/ui/etc/api/ui.api.md index 6f65b65b3..5caef6ada 100644 --- a/packages/ui/etc/api/ui.api.md +++ b/packages/ui/etc/api/ui.api.md @@ -33,6 +33,7 @@ import type { TableColumnRowSelectableFunc as TableColumnRowSelectableFunc_2 } f import type { TableColumnSortBy as TableColumnSortBy_2 } from '@/components/Table/types'; import type { TableColumnSortOrder as TableColumnSortOrder_2 } from '@/components/Table/types'; import { UnwrapRef } from 'vue'; +import type { ValidationsList } from '@/components/TextField/types'; import { VNodeProps } from 'vue'; // @public (undocumented) @@ -647,6 +648,7 @@ loading?: boolean | undefined; dropdownSearch?: boolean | undefined; remoteSearch?: boolean | undefined; maxShownOptions?: string | number | undefined; +mandatory?: boolean | undefined; }>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {}, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly[] | SelectOptionGroup[] | undefined; @@ -661,12 +663,14 @@ loading?: boolean | undefined; dropdownSearch?: boolean | undefined; remoteSearch?: boolean | undefined; maxShownOptions?: string | number | undefined; +mandatory?: boolean | undefined; }>>>, {}, {}>, { label?(_: { options: UnwrapRef[] | SelectOptionGroup[]>; multiple: boolean; disabled: boolean; loading: boolean; + mandatory: boolean; label: string | null; size: SelectSize; noAutoClose: boolean; @@ -702,6 +706,8 @@ export interface SelectApi extends UnwrapRef> { readonly label: string | null; // (undocumented) readonly loading: boolean; + // (undocumented) + readonly mandatory: boolean; menuToggle: (value?: boolean) => void; // (undocumented) readonly multiple: boolean; @@ -1283,6 +1289,7 @@ triggerSearch?: boolean | undefined; dropdownSearch?: boolean | undefined; remoteSearch?: boolean | undefined; maxShownOptions?: string | number | undefined; +mandatory?: boolean | undefined; }>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {}, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly[] | SelectOptionGroup[] | undefined; @@ -1297,12 +1304,14 @@ triggerSearch?: boolean | undefined; dropdownSearch?: boolean | undefined; remoteSearch?: boolean | undefined; maxShownOptions?: string | number | undefined; +mandatory?: boolean | undefined; }>>>, {}, {}>, { label?(_: { options: UnwrapRef[] | SelectOptionGroup[]>; multiple: boolean; disabled: boolean; loading: boolean; + mandatory: boolean; label: string | null; size: SelectSize; noAutoClose: boolean; @@ -1357,6 +1366,7 @@ sameWidthPopper: boolean; triggerSearch: boolean; dropdownSearch: boolean; remoteSearch: boolean; +mandatory: boolean; }>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, { "update:modelValue": (value: any) => void; search: (value: string) => void; @@ -1389,6 +1399,7 @@ sameWidthPopper: boolean; triggerSearch: boolean; dropdownSearch: boolean; remoteSearch: boolean; +mandatory: boolean; }>>> & { "onUpdate:modelValue"?: ((value: any) => any) | undefined; onSearch?: ((value: string) => any) | undefined; @@ -1400,6 +1411,7 @@ size: SelectSize; disabled: boolean; loading: boolean; options: SelectOption[] | SelectOptionGroup[]; +mandatory: boolean; syncMenuAndInputWidths: boolean; noAutoClose: boolean; sameWidthPopper: boolean; @@ -1436,6 +1448,7 @@ type: SelectButtonType; multiple: boolean; disabled: boolean; loading: boolean; + mandatory: boolean; label: string | null; size: SelectSize; noAutoClose: boolean; @@ -1481,6 +1494,7 @@ search: boolean; multiple: boolean; disabled: boolean; loading: boolean; + mandatory: boolean; label: string | null; size: SelectSize; noAutoClose: boolean; diff --git a/packages/ui/package.json b/packages/ui/package.json index 67b3385ac..aad6bdac6 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@soramitsu-ui/ui", - "version": "0.13.10", + "version": "0.13.13", "main": "dist/lib.cjs", "module": "dist/lib.mjs", "types": "dist/lib.d.ts", diff --git a/packages/ui/src/components/Popover/SPopoverWrappedTransition.vue b/packages/ui/src/components/Popover/SPopoverWrappedTransition.vue index af091cd41..86d44d0b1 100644 --- a/packages/ui/src/components/Popover/SPopoverWrappedTransition.vue +++ b/packages/ui/src/components/Popover/SPopoverWrappedTransition.vue @@ -1,14 +1,10 @@ - - -