Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pr/cdr 2153 refactor cdr input #39

Merged
merged 10 commits into from
Mar 11, 2022
2 changes: 1 addition & 1 deletion src/components/formError/CdrFormError.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div :class="style[baseClass]" role="status" tabindex="0">
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes the accessibility issue where the error was tabbable.

<div :class="style[baseClass]" role="status">
<span :class="style[iconClass]">
<icon-error-stroke size="small" inherit-color />
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ exports[`CdrFormError matches snapshot 1`] = `
<div
class="cdr-form-error"
role="status"
tabindex="0"
>
<span
class="cdr-form-error__icon"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ exports[`CdrFormGroup renders error state correctly 1`] = `
class="cdr-form-error"
id="renders-error"
role="status"
tabindex="0"
>
<span
class="cdr-form-error__icon"
Expand Down
170 changes: 82 additions & 88 deletions src/components/input/CdrInput.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<template>
<cdr-label-standalone
:for-id="id"
:for-id="uniqueId"
:label="label"
:hide-label="hideLabel"
:required="required"
:optional="optional"
:disabled="disabled"
:class="attrs.class"
>
<template
#helper
Expand Down Expand Up @@ -35,17 +36,16 @@
backgroundClass,
sizeClass,
)"
:id="id"
:id="uniqueId"
:disabled="disabled"
:aria-required="required || null"
:aria-invalid="!!error || null"
:aria-errormessage="(!!error && `${id}-error`) || null"
:aria-errormessage="(!!error && `${uniqueId}-error`) || null"
v-bind="inputAttrs"
:value="modelValue"
:aria-describedby="describedby || null"
@input="$emit('update:modelValue', $event.target.value)"
@focus="isFocused = true"
@blur="isFocused = false"
v-model="inputModel"
/>
<input
v-else
Expand All @@ -59,17 +59,16 @@
backgroundClass,
sizeClass,
)"
:id="id"
:id="uniqueId"
:disabled="disabled"
:aria-required="required || null"
:aria-invalid="!!error || null"
:aria-errormessage="(!!error && `${id}-error`) || null"
:aria-errormessage="(!!error && `${uniqueId}-error`) || null"
v-bind="inputAttrs"
:aria-describedby="describedby || null"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
@focus="isFocused = true"
@blur="isFocused = false"
v-model="inputModel"
>
<span
v-if="hasPreIcon"
Expand Down Expand Up @@ -107,7 +106,7 @@
<cdr-form-error
:error="error"
:role="errorRole"
:id="`${id}-error`"
:id="`${uniqueId}-error`"
v-if="error"
>
<template #error>
Expand All @@ -117,29 +116,30 @@
</template>
</cdr-label-standalone>
</template>

<script>
import { defineComponent, useCssModule, computed, ref } from 'vue';
export default {
inheritAttrs: false,
customOptions: {}
}
</script>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, I was wondering when we might run in to needing to do this with the setup syntax

<script setup>
import { useAttrs, useSlots, useCssModule, defineEmits, computed, ref } from 'vue';
import propValidator from '../../utils/propValidator';
import CdrLabelStandalone from '../labelStandalone/CdrLabelStandalone';
import CdrFormError from '../formError/CdrFormError';
import sizeProps from '../../props/size';
import backgroundProps from '../../props/background';
import mapClasses from '../../utils/mapClasses';
import uid from '../../utils/uid';

export default defineComponent({
name: 'CdrInput',
components: {
CdrLabelStandalone,
CdrFormError,
},
inheritAttrs: false,
props: {
const emit = defineEmits(['update:modelValue'])
const props = defineProps({
/**
* `id` for the input that is mapped to the label `for` attribute.
*/
id: {
type: String,
required: true,
type: String
},
/**
* 'type' attribute for the input as defined by w3c.
Expand All @@ -151,7 +151,6 @@ export default defineComponent({
default: 'text',
validator: (value) => propValidator(
value,
// TODO: moar types?
['text', 'email', 'number', 'password', 'search', 'url', 'tel'],
),
},
Expand Down Expand Up @@ -196,78 +195,73 @@ export default defineComponent({
required: Boolean,
optional: Boolean,
modelValue: {
type: [String, Number],
type: [String, Number, Function],
},
},
setup(props, ctx) {
const baseClass = 'cdr-input';
// console.log(ctx.slots['post-icon']);
// TODO: delete un-used hasSlot props
const isFocused = ref(false);
const hasHelperTop = ctx.slots['helper-text-top'];
const hasHelperBottom = ctx.slots['helper-text-bottom'];
const hasPreIcon = ctx.slots['pre-icon'];
const hasPostIcon = ctx.slots['post-icon'];
const hasPostIcons = hasPostIcon && ctx.slots['post-icon']().length > 1;
const hasInfo = ctx.slots.info;
const hasInfoAction = ctx.slots['info-action'];

const multilineClass = computed(() => props.rows > 1 && 'cdr-input--multiline');
const preIconClass = computed(() => hasPreIcon && 'cdr-input--preicon');
// TODO: make one class for this? if possible? there must have been a reason i did it like this.....
const postIconClass = computed(() => hasPostIcon && 'cdr-input--posticon');
const postIconsClass = computed(() => hasPostIcons && 'cdr-input--posticons');
const errorClass = computed(() => props.error && 'cdr-input--error');
const backgroundClass = computed(() => `cdr-input--${props.background}`);
const sizeClass = computed(() => props.size && `${baseClass}--${props.size}`);
const focusedClass = computed(() => isFocused.value && 'cdr-input--focus');
})
const baseClass = 'cdr-input';
// TODO: delete un-used hasSlot props
const isFocused = ref(false);
const slots = useSlots();
const attrs = useAttrs();
const hasHelperTop = slots['helper-text-top'];
const hasHelperBottom = slots['helper-text-bottom'];
const hasPreIcon = slots['pre-icon'];
const hasPostIcon = slots['post-icon'];
const hasPostIcons = hasPostIcon && slots['post-icon']().length > 1;
const hasInfo = slots.info;
const hasInfoAction = slots['info-action'];

const describedby = computed(() => {
return [
ctx.slots['helper-text-top'] ? `${props.id}-helper-text-top` : '',
ctx.slots['helper-text-bottom'] ? `${props.id}-helper-text-bottom` : '',
ctx.attrs['aria-describedby'],
].filter((x) => x).join(' ');
})
const uniqueId = props.id ? props.id : uid();
const multilineClass = computed(() => props.rows > 1 && 'cdr-input--multiline');
const preIconClass = computed(() => hasPreIcon && 'cdr-input--preicon');
// TODO: make one class for this? if possible? there must have been a reason i did it like this.....
const postIconClass = computed(() => hasPostIcon && 'cdr-input--posticon');
const postIconsClass = computed(() => hasPostIcons && 'cdr-input--posticons');
const errorClass = computed(() => props.error && 'cdr-input--error');
const backgroundClass = computed(() => `cdr-input--${props.background}`);
const sizeClass = computed(() => props.size && `${baseClass}--${props.size}`);
const focusedClass = computed(() => isFocused.value && 'cdr-input--focus');

const describedby = computed(() => {
return [
slots['helper-text-top'] ? `${uniqueId}-helper-text-top` : '',
slots['helper-text-bottom'] ? `${uniqueId}-helper-text-bottom` : '',
attrs['aria-describedby'],
].filter((x) => x).join(' ');
})

const inputAttrs = computed(() => {
const isNum = props.numeric || props.type === 'number';
return {
autocorrect: 'off',
spellcheck: 'false',
autocapitalize: 'off',
pattern: (isNum && '[0-9]*') || null,
inputmode: (isNum && 'numeric') || null,
novalidate: isNum || null,
...ctx.attrs,
};
});
const attrsWithClassExcluded = computed(()=>{
let returnObj = {}
for (const attr in attrs) {
if (attr !== 'class') {
returnObj[attr] = attrs[attr]
}
}
return returnObj;
})

return {
style: useCssModule(),
baseClass,
sizeClass,
focusedClass,
multilineClass,
preIconClass,
postIconClass,
postIconsClass,
errorClass,
backgroundClass,
isFocused,
hasHelperTop,
hasHelperBottom,
hasPreIcon,
hasPostIcon,
hasInfo,
hasInfoAction,
inputAttrs,
describedby,
mapClasses,
};
},
const inputAttrs = computed(() => {
const isNum = props.numeric || props.type === 'number';
return {
id: uniqueId,
autocorrect: 'off',
spellcheck: 'false',
autocapitalize: 'off',
pattern: (isNum && '[0-9]*') || null,
inputmode: (isNum && 'numeric') || null,
novalidate: isNum || null,
...attrsWithClassExcluded.value,
};
});
const style = useCssModule();
const inputModel = computed({
get() {
return props.modelValue
},
set(newValue) {
emit('update:modelValue', newValue)
}
})
</script>
<style lang="scss" module src="./styles/CdrInput.module.scss">
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ exports[`CdrInput renders correctly 1`] = `
<!--v-if-->
</div>
<div
class="cdr-label-standalone__input-wrap cdr-label-standalone__input--spacing"
class="cdr-label-standalone__input-wrap cdr-label-standalone__input-spacing"
>

<div
Expand Down Expand Up @@ -68,7 +68,7 @@ exports[`CdrInput renders error state correctly 1`] = `
<!--v-if-->
</div>
<div
class="cdr-label-standalone__input-wrap cdr-label-standalone__input--spacing"
class="cdr-label-standalone__input-wrap cdr-label-standalone__input-spacing"
>

<div
Expand Down Expand Up @@ -101,7 +101,6 @@ exports[`CdrInput renders error state correctly 1`] = `
class="cdr-form-error"
id="renders-error"
role="status"
tabindex="0"
>
<span
class="cdr-form-error__icon"
Expand Down Expand Up @@ -159,7 +158,7 @@ exports[`CdrInput renders number input correctly 1`] = `
<!--v-if-->
</div>
<div
class="cdr-label-standalone__input-wrap cdr-label-standalone__input--spacing"
class="cdr-label-standalone__input-wrap cdr-label-standalone__input-spacing"
>

<div
Expand Down
Loading