diff --git a/.changeset/quiet-badgers-beg.md b/.changeset/quiet-badgers-beg.md new file mode 100644 index 00000000..bc58d3dc --- /dev/null +++ b/.changeset/quiet-badgers-beg.md @@ -0,0 +1,5 @@ +--- +'@soramitsu-ui/ui': patch +--- + +**refactor**(`STextField`, `SSwitch`, `SUseNotification`, `SNotificationBody`, `SModal`, `SModalCard`, `SAlert`): set default values for optional props diff --git a/.changeset/quiet-ghosts-hug.md b/.changeset/quiet-ghosts-hug.md index 61d478c8..97131dbe 100644 --- a/.changeset/quiet-ghosts-hug.md +++ b/.changeset/quiet-ghosts-hug.md @@ -2,4 +2,4 @@ '@soramitsu-ui/ui': minor --- -**refactor!**(`SSwitch`): remove `id` prop (it is generated automatically inside now); make `label` prop required - a11y first; add `label-hidden` optional bool prop in order to _visually_ hide the `label` (`false` by default); update doc comments +**feature**(`SSwitch`): add `label-hidden` optional bool prop in order to _visually_ hide the `label` (`false` by default); update doc comments diff --git a/.changeset/spicy-rivers-run.md b/.changeset/spicy-rivers-run.md new file mode 100644 index 00000000..7f0969c4 --- /dev/null +++ b/.changeset/spicy-rivers-run.md @@ -0,0 +1,5 @@ +--- +'@soramitsu-ui/ui': minor +--- + +**feature**(`STextField`, `SSwitch`): use auto-generated `id` if not provided diff --git a/.changeset/tricky-seas-knock.md b/.changeset/tricky-seas-knock.md new file mode 100644 index 00000000..e0537726 --- /dev/null +++ b/.changeset/tricky-seas-knock.md @@ -0,0 +1,5 @@ +--- +'@soramitsu-ui/ui': patch +--- + +**perf**(`STextField`): use `computedEager` for cheap computeds; use `shallowRef` instead of `ref` diff --git a/.eslintrc.js b/.eslintrc.js index 96f30949..5fbdeed8 100755 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -24,11 +24,13 @@ module.exports = { '@typescript-eslint/consistent-type-definitions': 'off', - // FIXME - 'vue/require-default-prop': 'off', - - // FIXME - 'vuejs-accessibility/no-static-element-interactions': 'off', + 'vuejs-accessibility/label-has-for': [ + 'error', + { + // all labels should have `for` attr + required: 'id', + }, + ], }, overrides: [ { @@ -43,38 +45,57 @@ module.exports = { files: ['**/packages/ui/**/*.{ts,vue,js}'], extends: ['./packages/ui/.eslintrc-auto-import.json'], }, - { - files: ['**/*.spec.{js,ts}'], - env: { - jest: true, - }, - }, - - // It is OK to define a lot of components in stories or tests { files: ['**/packages/ui/stories/**/*.stories.ts', '**/*.cy.{js,ts}'], rules: { + // It is OK to define a lot of components in stories or tests 'vue/one-component-per-file': 'off', + + // We don't need such strictness in stories + 'vue/require-prop-types': 'off', }, }, { files: ['**/*.spec.ts', '**/*.spec.cy.ts'], rules: { 'max-nested-callbacks': 'off', + + // We don't need such strictness in tests + 'vue/require-default-prop': 'off', + 'vue/require-prop-types': 'off', }, }, - // FIXME - temporary disables to fix them in a different PRs { files: ['**/ui/src/components/Select/**/*.vue'], rules: { + // FIXME https://github.com/soramitsu/soramitsu-js-ui-library/issues/525 'vuejs-accessibility/click-events-have-key-events': 'off', + 'vuejs-accessibility/no-static-element-interactions': 'off', + }, + }, + + { + files: ['**/ui/src/components/DatePicker/**/*.vue'], + rules: { + // FIXME https://github.com/soramitsu/soramitsu-js-ui-library/issues/526 + 'vuejs-accessibility/no-static-element-interactions': 'off', + }, + }, + + { + files: ['**/ui/src/components/Table/**/*.vue'], + rules: { + // FIXME https://github.com/soramitsu/soramitsu-js-ui-library/issues/527 + 'vuejs-accessibility/no-static-element-interactions': 'off', }, }, + + // FIXME https://github.com/soramitsu/soramitsu-js-ui-library/issues/528 { - files: ['**/STextField.vue', '**/SSwitch.vue'], + files: ['**/ui/src/components/JsonInput/**/*.vue'], rules: { - 'vuejs-accessibility/label-has-for': 'off', + 'vuejs-accessibility/no-static-element-interactions': 'off', }, }, ], diff --git a/packages/ui/src/components/Alert/SAlert.vue b/packages/ui/src/components/Alert/SAlert.vue index 789d9549..120e9a05 100644 --- a/packages/ui/src/components/Alert/SAlert.vue +++ b/packages/ui/src/components/Alert/SAlert.vue @@ -13,6 +13,8 @@ type Props = { const props = withDefaults(defineProps(), { status: Status.Info, showCloseBtn: false, + title: undefined, + description: undefined, }) const emit = defineEmits<(event: 'click:close') => void>() diff --git a/packages/ui/src/components/Modal/SModal.vue b/packages/ui/src/components/Modal/SModal.vue index bd41f0ee..123fbe92 100644 --- a/packages/ui/src/components/Modal/SModal.vue +++ b/packages/ui/src/components/Modal/SModal.vue @@ -111,6 +111,12 @@ const props = withDefaults(defineProps(), { // here is a Vue typing error - primitive value factory is a valid default value uniqueElementId as unknown as string, describedBy: null, + rootClass: undefined, + modalClass: undefined, + overlayClass: undefined, + rootStyle: undefined, + modalStyle: undefined, + overlayStyle: undefined, }) const emit = defineEmits(['update:show', 'click:overlay', 'before-open', 'after-open', 'before-close', 'after-close']) @@ -248,7 +254,10 @@ useCloseOnEsc( v-bind="overlayTransitionAttrs" v-on="overlayTransitionListeners" > - + +
(), { close: true, + title: undefined, }, ) diff --git a/packages/ui/src/components/Notifications/SNotificationBody.vue b/packages/ui/src/components/Notifications/SNotificationBody.vue index 3aca544f..20bfd083 100644 --- a/packages/ui/src/components/Notifications/SNotificationBody.vue +++ b/packages/ui/src/components/Notifications/SNotificationBody.vue @@ -14,6 +14,8 @@ const props = withDefaults( { status: Status.Info, timeout: 0, + title: undefined, + description: undefined, }, ) diff --git a/packages/ui/src/components/Notifications/SUseNotification.ts b/packages/ui/src/components/Notifications/SUseNotification.ts index cf744a9d..eae9a09e 100644 --- a/packages/ui/src/components/Notifications/SUseNotification.ts +++ b/packages/ui/src/components/Notifications/SUseNotification.ts @@ -10,7 +10,7 @@ export default /* @__PURE__ */ defineComponent({ name: 'SUseNotification', props: { show: Boolean, - title: String, + title: { type: String, default: undefined }, status: { type: String as PropType, default: Status.Info, @@ -20,7 +20,7 @@ export default /* @__PURE__ */ defineComponent({ default: 5000, }, showCloseBtn: Boolean, - description: String, + description: { type: String, default: undefined }, }, emits: [ // FIXME avoid `v-model` for `show`, because it always emits `false` from the component diff --git a/packages/ui/src/components/Switch/SSwitch.vue b/packages/ui/src/components/Switch/SSwitch.vue index 48461f5f..61d00eb2 100644 --- a/packages/ui/src/components/Switch/SSwitch.vue +++ b/packages/ui/src/components/Switch/SSwitch.vue @@ -1,5 +1,6 @@ diff --git a/packages/ui/src/components/TextField/STextField.vue b/packages/ui/src/components/TextField/STextField.vue index 9281a8cd..59982523 100644 --- a/packages/ui/src/components/TextField/STextField.vue +++ b/packages/ui/src/components/TextField/STextField.vue @@ -9,6 +9,7 @@ import type { StyleValue } from 'vue' import { Status } from '@/types' import { STATUS_ICONS_MAP_16, IconEye, IconEyeOff } from '../icons' import type { MaybeElementRef } from '@vueuse/core' +import { useElementIdFallback } from '@/composables/element-id-fallback' /** * warning: don't use it inside of `Props`. Vue compiler determines it @@ -42,7 +43,7 @@ type Props = { label?: string /** - * Recommended for a11y + * Can be passed to override default auto-generated id */ id?: string @@ -119,6 +120,11 @@ const props = withDefaults(defineProps(), { noEye: false, noModelValueStrictSync: false, filledState: false, + message: undefined, + status: undefined, + id: undefined, + modelValue: '', + label: undefined, }) const emit = defineEmits<{ @@ -130,7 +136,7 @@ const slots = useSlots() // *** -const status = computed(() => { +const status = computedEager(() => { if (props.status) return props.status if (props.success) return Status.Success if (props.warning) return Status.Warning @@ -148,13 +154,13 @@ function onInput(e: Event) { } } -const isValueEmpty = computed(() => !model.value) +const isValueEmpty = computedEager(() => !model.value) const isFocused = ref(false) -const labelTypographyClass = computed(() => +const labelTypographyClass = computedEager(() => !(props.filledState || isFocused.value) && isValueEmpty.value ? 'sora-tpg-p3' : 'sora-tpg-p4', ) -const inputRef = ref(null) +const inputRef = shallowRef(null) function handleInputWrapperClick(event: MouseEvent) { if (event.target !== document.activeElement) { @@ -206,7 +212,7 @@ const counterConfig = computed(() => { return null }) -const counterText = computed(() => { +const counterText = computedEager(() => { const config = counterConfig.value if (!config) return null const { limit } = config @@ -226,16 +232,20 @@ const inputAttrs = () => { // APPEND -const showEye = computed(() => props.password && !props.noEye) +const showEye = computedEager(() => props.password && !props.noEye) const isAppendSlotDefined = () => !!slots.append const shouldRenderAppend = () => !!counterText.value || isAppendSlotDefined() || showEye.value // EYE const [forceRevealPassword, toggleForceReveal] = useToggle() -const inputType = computed(() => +const inputType = computedEager(() => props.password && (!showEye.value || !forceRevealPassword.value) ? 'password' : 'text', ) + +// A11Y + +const finalId = useElementIdFallback(toRef(props, 'id'))