Merge branch 'develop' into feature/digital/SSR-540
This commit is contained in:
commit
0434edcf0f
31 changed files with 3916 additions and 1786 deletions
65
.eslintrc.js
65
.eslintrc.js
|
|
@ -1,21 +1,46 @@
|
|||
module.exports = {
|
||||
extends: [
|
||||
'eslint-config-airbnb-base',
|
||||
'plugin:vue/vue3-strongly-recommended'
|
||||
],
|
||||
rules: {
|
||||
'linebreak-style': 'off',
|
||||
'vue/component-definition-name-casing': ['warn', 'kebab-case'],
|
||||
'vue/require-default-prop': 'off',
|
||||
'vue/attribute-hyphenation': ['warn', 'never'],
|
||||
'vue/v-on-event-hyphenation': ['warn', 'never']
|
||||
},
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
alias: {
|
||||
map: [['@', './src/']],
|
||||
extensions: ['.js', '.vue']
|
||||
}
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
jest: true
|
||||
},
|
||||
extends: [
|
||||
'eslint-config-airbnb-base',
|
||||
'plugin:vue/vue3-recommended'
|
||||
],
|
||||
rules: {
|
||||
'linebreak-style': 'off',
|
||||
'vue/component-definition-name-casing': ['warn', 'kebab-case'],
|
||||
'vue/require-default-prop': 'off',
|
||||
'vue/attribute-hyphenation': ['warn', 'never'],
|
||||
'vue/v-on-event-hyphenation': ['warn', 'never'],
|
||||
'object-curly-newline': ['error', { consistent: true }],
|
||||
'function-paren-newline': ['error', 'never'],
|
||||
'operator-linebreak': ['error', 'before'],
|
||||
'implicit-arrow-linebreak': ['error', 'below'],
|
||||
'comma-dangle': ['error', 'never'],
|
||||
indent: ['error', 4],
|
||||
'vue/html-indent': 'off',
|
||||
'vue/html-closing-bracket-newline': ['error', {
|
||||
singleline: 'never',
|
||||
multiline: 'never'
|
||||
}],
|
||||
'vue/html-self-closing': ['error', {
|
||||
html: {
|
||||
void: 'any',
|
||||
normal: 'any',
|
||||
component: 'any'
|
||||
},
|
||||
svg: 'always',
|
||||
math: 'always'
|
||||
}],
|
||||
'import/extensions': ['error', 'always', { vue: 'never' }]
|
||||
},
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
alias: {
|
||||
map: [['@', './src/']],
|
||||
extensions: ['.js', '.vue']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
|||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -26,3 +26,5 @@ pnpm-debug.log*
|
|||
coverage/*
|
||||
junit.xml
|
||||
/.vs
|
||||
|
||||
.prettierrc
|
||||
|
|
|
|||
1036
package-lock.json
generated
1036
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -40,6 +40,7 @@
|
|||
"eslint-plugin-vue": "9.8.0",
|
||||
"jest": "^27.0.5",
|
||||
"sass": "^1.32.7",
|
||||
"sass-loader": "^12.0.0"
|
||||
"sass-loader": "^12.0.0",
|
||||
"vitest": "^0.32.4"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
22
src/constants/global-rules.js
Normal file
22
src/constants/global-rules.js
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* @file global-rules.js
|
||||
* @author MB
|
||||
* @copyright Safelite
|
||||
*/
|
||||
|
||||
/**
|
||||
* @summary Contains all the globally defined rules.
|
||||
*/
|
||||
const globalRules = {
|
||||
POLICYHOLDER_FIRST_NAME_REQUIRED: 'policyholder-first-name-required',
|
||||
POLICYHOLDER_LAST_NAME_REQUIRED: 'policyholder-last-name-required',
|
||||
FIRST_NAME_REQUIRED: 'first-name-required',
|
||||
LAST_NAME_REQUIRED: 'last-name-required',
|
||||
PHONE_NUMBER_REQUIRED: 'phone-number-required',
|
||||
EMAIL_ADDRESS_REQUIRED: 'email-required',
|
||||
EMAIL_ADDRESS_FORMAT: 'email-address-format',
|
||||
PHONE_NUMBER_FORMAT: 'phone-number-format',
|
||||
OPTION_REQUIRED: 'option-required'
|
||||
};
|
||||
|
||||
export default globalRules;
|
||||
|
|
@ -0,0 +1,348 @@
|
|||
// Components
|
||||
import textareaQuestion from '@/digital-components/textarea-question/textarea-question';
|
||||
|
||||
// Supporting Files
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { getRandomString, getRandomInt } from '@/helpers/data-generation.js';
|
||||
import { nextTick } from 'vue';
|
||||
import { defineRule } from 'vee-validate';
|
||||
import { required } from '@/helpers/validation-rules.js';
|
||||
|
||||
describe('textarea-question.vue', () => {
|
||||
const mockMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn().mockImplementation(() =>
|
||||
getRandomString(10, 20)),
|
||||
setCmsContent: jest.fn()
|
||||
}
|
||||
};
|
||||
describe('Expected components rendered', () => {
|
||||
test('Label rendered', () => {
|
||||
// Arrange
|
||||
const questionText = getRandomString(10, 20);
|
||||
const mixinWithQuestion = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn().mockImplementation(() =>
|
||||
questionText),
|
||||
setCmsContent: jest.fn()
|
||||
}
|
||||
};
|
||||
const wrapper = shallowMount(textareaQuestion, {
|
||||
propsData: {
|
||||
inputId: getRandomString(10, 20),
|
||||
cmsWidgetName: getRandomString(10, 20)
|
||||
},
|
||||
mixins: [mixinWithQuestion]
|
||||
});
|
||||
|
||||
// Act
|
||||
const label = wrapper.find('label');
|
||||
|
||||
// Assert
|
||||
expect(label.exists()).toBe(true);
|
||||
expect(wrapper.text()).toContain(questionText);
|
||||
});
|
||||
test('Textarea rendered', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(textareaQuestion, {
|
||||
propsData: {
|
||||
inputId: getRandomString(10, 20),
|
||||
cmsWidgetName: getRandomString(10, 20)
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
// Act
|
||||
const textarea = wrapper.find('textarea');
|
||||
|
||||
// Assert
|
||||
expect(textarea.exists()).toBe(true);
|
||||
});
|
||||
test('Character count rendered', () => {
|
||||
// Arrange
|
||||
const maxLength = getRandomInt(100, 900);
|
||||
const wrapper = shallowMount(textareaQuestion, {
|
||||
propsData: {
|
||||
inputId: getRandomString(10, 20),
|
||||
cmsWidgetName: getRandomString(10, 20),
|
||||
maxLength
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
const expected = `${maxLength}/${maxLength}`;
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
expect(wrapper.text()).toContain(expected);
|
||||
});
|
||||
test('Error rendered', async () => {
|
||||
// Arrange
|
||||
const maxLength = getRandomInt(100, 900);
|
||||
const wrapper = shallowMount(textareaQuestion, {
|
||||
propsData: {
|
||||
inputId: getRandomString(10, 20),
|
||||
cmsWidgetName: getRandomString(10, 20),
|
||||
maxLength
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
// Act
|
||||
const errorComponent = wrapper.find('#error-message');
|
||||
expect(errorComponent.text()).toBe('');
|
||||
|
||||
const errorMessage = getRandomString(20, 30);
|
||||
wrapper.vm.resetField({
|
||||
errors: [errorMessage]
|
||||
});
|
||||
await nextTick();
|
||||
|
||||
// Assert
|
||||
expect(errorComponent.text()).toBe(errorMessage);
|
||||
});
|
||||
test('Not required => optional text rendered', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(textareaQuestion, {
|
||||
propsData: {
|
||||
inputId: getRandomString(10, 20),
|
||||
cmsWidgetName: getRandomString(10, 20),
|
||||
isRequired: false
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
expect(wrapper.text()).toContain('Optional');
|
||||
});
|
||||
test('Required => optional text not rendered', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(textareaQuestion, {
|
||||
propsData: {
|
||||
inputId: getRandomString(10, 20),
|
||||
cmsWidgetName: getRandomString(10, 20),
|
||||
isRequired: true
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
expect(wrapper.text()).not.toContain('Optional');
|
||||
});
|
||||
});
|
||||
describe('Computed properties rendered', () => {
|
||||
describe('characterCount', () => {
|
||||
test('Model value null => character count is 0 ', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(textareaQuestion, {
|
||||
propsData: {
|
||||
inputId: getRandomString(10, 20),
|
||||
cmsWidgetName: getRandomString(10, 20),
|
||||
modelValue: null
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
expect(wrapper.vm.characterCount).toBe(0);
|
||||
});
|
||||
test('Model value undefined => character count is 0', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(textareaQuestion, {
|
||||
propsData: {
|
||||
inputId: getRandomString(10, 20),
|
||||
cmsWidgetName: getRandomString(10, 20),
|
||||
modelValue: undefined
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
expect(wrapper.vm.characterCount).toBe(0);
|
||||
});
|
||||
test('Model value empty string => character count is 0', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(textareaQuestion, {
|
||||
propsData: {
|
||||
inputId: getRandomString(10, 20),
|
||||
cmsWidgetName: getRandomString(10, 20),
|
||||
modelValue: ''
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
expect(wrapper.vm.characterCount).toBe(0);
|
||||
});
|
||||
test('Model value non-empty string => character count is as expected', () => {
|
||||
// Arrange
|
||||
const length = getRandomInt(10, 30);
|
||||
const value = getRandomString(length, length);
|
||||
const wrapper = shallowMount(textareaQuestion, {
|
||||
propsData: {
|
||||
inputId: getRandomString(10, 20),
|
||||
cmsWidgetName: getRandomString(10, 20),
|
||||
modelValue: value
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
expect(wrapper.vm.characterCount).toBe(length);
|
||||
});
|
||||
});
|
||||
describe('value', () => {
|
||||
test('Get value corresponds to modelValue', () => {
|
||||
// Arrange
|
||||
const modelValue = getRandomString(10, 20);
|
||||
const wrapper = shallowMount(textareaQuestion, {
|
||||
propsData: {
|
||||
inputId: getRandomString(10, 20),
|
||||
cmsWidgetName: getRandomString(10, 20),
|
||||
modelValue
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
expect(wrapper.vm.value).toBe(modelValue);
|
||||
});
|
||||
test('Set value => update modelValue event emitted with new value', () => {
|
||||
// Arrange
|
||||
const modelValue = getRandomString(10, 20);
|
||||
const wrapper = shallowMount(textareaQuestion, {
|
||||
propsData: {
|
||||
inputId: getRandomString(10, 20),
|
||||
cmsWidgetName: getRandomString(10, 20),
|
||||
modelValue
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
const newValue = getRandomString(10, 20);
|
||||
|
||||
// Act
|
||||
wrapper.vm.value = newValue;
|
||||
|
||||
// Assert
|
||||
expect(wrapper.emitted('update:modelValue')).toBeTruthy();
|
||||
expect(wrapper.emitted('update:modelValue')[0][0]).toEqual(newValue);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Properties properly affect initial state', () => {
|
||||
test('Placeholder text sets placeholder text of text area', () => {
|
||||
// Arrange
|
||||
const placeholderText = getRandomString(10, 20);
|
||||
const wrapper = shallowMount(textareaQuestion, {
|
||||
propsData: {
|
||||
inputId: getRandomString(10, 20),
|
||||
cmsWidgetName: getRandomString(10, 20),
|
||||
placeholderText
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
// Act
|
||||
const textarea = wrapper.find('textarea');
|
||||
|
||||
// Assert
|
||||
expect(textarea.attributes().placeholder).toBe(placeholderText);
|
||||
});
|
||||
test('isDisabled true => textarea disabled', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(textareaQuestion, {
|
||||
propsData: {
|
||||
inputId: getRandomString(10, 20),
|
||||
cmsWidgetName: getRandomString(10, 20),
|
||||
isDisabled: true
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
// Act
|
||||
const textarea = wrapper.find('textarea');
|
||||
|
||||
// Assert
|
||||
expect(textarea.attributes().disabled).toBeDefined();
|
||||
});
|
||||
test('isRequired true => textarea required', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(textareaQuestion, {
|
||||
propsData: {
|
||||
inputId: getRandomString(10, 20),
|
||||
cmsWidgetName: getRandomString(10, 20),
|
||||
isRequired: true
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
// Act
|
||||
const textarea = wrapper.find('textarea');
|
||||
|
||||
// Assert
|
||||
expect(textarea.attributes().required).toBeDefined();
|
||||
});
|
||||
test('validationRules set rules of text area', () => {
|
||||
// Arrange
|
||||
const rule = getRandomString(10, 20);
|
||||
const errorMessage = getRandomString(20, 30);
|
||||
defineRule(rule, required(errorMessage));
|
||||
const wrapper = shallowMount(textareaQuestion, {
|
||||
propsData: {
|
||||
inputId: getRandomString(10, 20),
|
||||
cmsWidgetName: getRandomString(10, 20),
|
||||
validationRules: rule
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
// Act
|
||||
const textarea = wrapper.find('textarea');
|
||||
|
||||
// Assert
|
||||
expect(textarea.attributes().validationrules).toBe(rule);
|
||||
});
|
||||
test('maxLength set max length of text area', () => {
|
||||
// Arrange
|
||||
const maxLength = toString(getRandomInt(100, 900));
|
||||
const wrapper = shallowMount(textareaQuestion, {
|
||||
propsData: {
|
||||
inputId: getRandomString(10, 20),
|
||||
cmsWidgetName: getRandomString(10, 20),
|
||||
maxLength
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
// Act
|
||||
const textarea = wrapper.find('textarea');
|
||||
|
||||
// Assert
|
||||
expect(textarea.attributes().maxlength).toBe(maxLength);
|
||||
});
|
||||
test('inputRows set input rows of text area', () => {
|
||||
// Arrange
|
||||
const inputRows = getRandomInt(10, 20);
|
||||
const wrapper = shallowMount(textareaQuestion, {
|
||||
propsData: {
|
||||
inputId: getRandomString(10, 20),
|
||||
cmsWidgetName: getRandomString(10, 20),
|
||||
inputRows
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
// Act
|
||||
const textarea = wrapper.find('textarea');
|
||||
|
||||
// Assert
|
||||
expect(textarea.attributes().rows).toBe(`${inputRows}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
197
src/digital-components/textarea-question/textarea-question.vue
Normal file
197
src/digital-components/textarea-question/textarea-question.vue
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
<template>
|
||||
<div
|
||||
class="textarea-question"
|
||||
:class="(errors && errors.length) ? 'has-error' : ''">
|
||||
<label
|
||||
:for="inputId"
|
||||
class="form-label">
|
||||
{{ questionText }}
|
||||
<span
|
||||
v-if="!isRequired"
|
||||
class="optional">(Optional)</span>
|
||||
</label>
|
||||
<div class="input-wrapper">
|
||||
<textarea
|
||||
:id="inputId"
|
||||
:ref="inputId"
|
||||
v-model.trim="value"
|
||||
class="form-control"
|
||||
:name="inputId"
|
||||
:rows="[inputRows ?? 10]"
|
||||
:placeholder="placeholderText"
|
||||
:disabled="isDisabled"
|
||||
:required="isRequired"
|
||||
:maxlength="maxLength"
|
||||
:validationRules="validationRules"
|
||||
@change="validationRules ? handleChange : () => {}"
|
||||
@blur="validationRules ? handleBlur : () => {}"
|
||||
@paste="trimOnPaste"
|
||||
@drop="trimOnPaste">
|
||||
</textarea>
|
||||
</div>
|
||||
<p class="character-count margin-top-8">
|
||||
{{ maxLength - characterCount }}/{{ maxLength }} characters remaining
|
||||
</p>
|
||||
<div
|
||||
v-show="errorMessage"
|
||||
id="error-message"
|
||||
class="row my-1 form-test-error">
|
||||
<span
|
||||
class="d-inline-flex mt-0"
|
||||
role="alert">{{ errorMessage }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useField } from 'vee-validate';
|
||||
|
||||
export default {
|
||||
name: 'textarea-question',
|
||||
props: {
|
||||
placeholderText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
inputId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
modelValue: String,
|
||||
isDisabled: Boolean,
|
||||
isRequired: Boolean,
|
||||
validationRules: String,
|
||||
cmsWidgetName: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
maxLength: {
|
||||
type: String,
|
||||
default: '300'
|
||||
},
|
||||
inputRows: Number
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup(props) {
|
||||
const propsClone = { ...props };
|
||||
const { modelValue } = propsClone;
|
||||
const initialValue = modelValue ?? '';
|
||||
|
||||
const fieldOptions = {
|
||||
type: 'text',
|
||||
value: modelValue,
|
||||
initialValue
|
||||
};
|
||||
|
||||
const { errorMessage,
|
||||
handleChange,
|
||||
handleBlur,
|
||||
validate,
|
||||
errors,
|
||||
resetField }
|
||||
= useField(props.inputId,
|
||||
props.validationRules,
|
||||
fieldOptions);
|
||||
|
||||
return {
|
||||
errorMessage,
|
||||
handleChange,
|
||||
handleBlur,
|
||||
validate,
|
||||
errors,
|
||||
resetField
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* @summary Returns the number of characters in the textarea field.
|
||||
*/
|
||||
characterCount() {
|
||||
return this?.modelValue?.length ?? 0;
|
||||
},
|
||||
/**
|
||||
* @summary Returns the CMS text associated with the question.
|
||||
*/
|
||||
questionText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
|
||||
},
|
||||
value: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(newValue) {
|
||||
this.$emit('update:modelValue', newValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* @summary Removes whitespace from the end of pasted content.
|
||||
*/
|
||||
trimOnPaste(evt) {
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
|
||||
const data = evt.type === 'paste' ? (evt.clipboardData || window.clipboardData) : evt.dataTransfer;
|
||||
const value = data.getData('Text')?.trim();
|
||||
|
||||
this.$emit('update:modelValue', this.modelValue + value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.textarea-question {
|
||||
label {
|
||||
color: $black;
|
||||
font-weight: 500;
|
||||
}
|
||||
.form-test-error span{
|
||||
color: $red;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
max-height: 30rem;
|
||||
border: 1px solid $gray-500;
|
||||
border-radius: 0.5rem;
|
||||
padding: 12px 16px;
|
||||
&::placeholder {
|
||||
color: $gray-500;
|
||||
}
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 2.5px $blue;
|
||||
}
|
||||
&:disabled,
|
||||
&.disabled {
|
||||
background-color: $gray-100;
|
||||
&:hover {
|
||||
box-shadow: 0 0 0 4px transparent;
|
||||
border: 1px solid $gray-500;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
border: 1px solid $gray-500;
|
||||
box-shadow: 0 0 0 4px $blue-300;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.optional {
|
||||
color: $gray-550;
|
||||
}
|
||||
|
||||
.character-count {
|
||||
color: $gray-600;
|
||||
font-size: 12px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.margin-top-8 {
|
||||
margin-top: 8px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
includeSearchIcon ? 'has-search-icon' : '',
|
||||
includeSelectIcon ? 'has-select-icon' : '',
|
||||
]">
|
||||
<input
|
||||
<input
|
||||
class="form-control"
|
||||
v-model.trim.lazy="value"
|
||||
v-maska="mask"
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
:aria-disabled="isDisabled"
|
||||
:disabled="isDisabled"
|
||||
:aria-required="isRequired"
|
||||
:aria-label="questionText"
|
||||
:aria-label="questionText"
|
||||
:min="min"
|
||||
:max="max"
|
||||
required
|
||||
|
|
@ -38,14 +38,18 @@
|
|||
@change="validationRules ? handleChange : () => {}"
|
||||
@blur="validationRules ? handleChange : () => {}"
|
||||
:maxlength="maxLength ? maxLength : '999'"
|
||||
@focus="$emit('focus', $event.target.value)"
|
||||
@focus="$emit('focus', $event.target.value)"
|
||||
:data-bs-toggle="includeSelectIcon ? 'modal' : ''"
|
||||
:data-bs-target="'#' + this.cmsWidgetName"
|
||||
:data-bs-target="'#' + this.cmsWidgetName"
|
||||
@paste="trimOnPaste"
|
||||
@drop="trimOnPaste"/>
|
||||
@drop="trimOnPaste" />
|
||||
<button v-if="includeSearchIcon" type="submit" aria-label="Search button" />
|
||||
<button v-if="includeSelectIcon" type="submit" data-bs-toggle="modal"
|
||||
:data-bs-target="'#' + this.cmsWidgetName" aria-label="Select button" />
|
||||
<button
|
||||
v-if="includeSelectIcon"
|
||||
type="submit"
|
||||
data-bs-toggle="modal"
|
||||
:data-bs-target="'#' + this.cmsWidgetName"
|
||||
aria-label="Select button" />
|
||||
</div>
|
||||
<div v-show="errorMessage" class="row my-1 form-test-error">
|
||||
<span class="d-inline-flex mt-0" role="alert">{{ errorMessage }}</span>
|
||||
|
|
@ -94,7 +98,7 @@ export default {
|
|||
includeSelectIcon: Boolean,
|
||||
min: String,
|
||||
max: String,
|
||||
disableAutoFill: Boolean
|
||||
disableAutoFill: Boolean,
|
||||
},
|
||||
setup(props) {
|
||||
const propsClone = Object.assign({}, props);
|
||||
|
|
@ -134,17 +138,22 @@ export default {
|
|||
methods: {
|
||||
trimOnPaste(evt) {
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
|
||||
const data = evt.type == "paste" ? (evt.clipboardData || window.clipboardData) : evt.dataTransfer;
|
||||
const value = data.getData('Text')?.trim();
|
||||
var data = null;
|
||||
if (evt.type === "paste") {
|
||||
data = evt.clipboardData || window.clipboardData;
|
||||
} else {
|
||||
evt.preventDefault();
|
||||
data = evt.dataTransfer;
|
||||
}
|
||||
|
||||
const value = data.getData("Text")?.trim();
|
||||
|
||||
this.$emit("update:modelValue", value);
|
||||
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
questionText() {
|
||||
questionText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, "QuestionText");
|
||||
},
|
||||
value: {
|
||||
|
|
@ -195,27 +204,26 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
input[type="date"]::-webkit-inner-spin-button{
|
||||
input[type="date"]::-webkit-inner-spin-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
input[type="date"]::-webkit-calendar-picker-indicator {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: 1px;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.1762 1.52764H13.7656V1.50352C13.7656 1.10476 13.6076 0.722334 13.3263 0.44037C13.0451 0.158406 12.6636 0 12.2659 0C11.8681 0 11.4866 0.158406 11.2054 0.44037C10.9241 0.722334 10.7661 1.10476 10.7661 1.50352V1.52764H5.42476V1.50352C5.42476 1.10476 5.26675 0.722334 4.9855 0.44037C4.70424 0.158406 4.32277 0 3.92501 0C3.52725 0 3.14579 0.158406 2.86453 0.44037C2.58327 0.722334 2.42526 1.10476 2.42526 1.50352V1.52764H1.82376C1.34046 1.52891 0.877316 1.72195 0.53557 2.06455C0.193824 2.40716 0.00127018 2.87146 0 3.35598V14.1717C0.0016909 14.656 0.194379 15.1201 0.536035 15.4626C0.87769 15.8051 1.34059 15.9983 1.82376 16H14.1746C14.6581 15.9987 15.1214 15.8057 15.4634 15.4632C15.8054 15.1206 15.9983 14.6563 16 14.1717V3.35598C15.9987 2.87146 15.8062 2.40716 15.4644 2.06455C15.1227 1.72195 14.6595 1.52891 14.1762 1.52764ZM11.8889 1.50352C11.8889 1.4033 11.9286 1.30718 11.9993 1.23631C12.07 1.16544 12.1659 1.12563 12.2659 1.12563C12.3658 1.12563 12.4617 1.16544 12.5324 1.23631C12.6031 1.30718 12.6428 1.4033 12.6428 1.50352V2.99899C12.6428 3.09922 12.6031 3.19534 12.5324 3.2662C12.4617 3.33707 12.3658 3.37688 12.2659 3.37688C12.1659 3.37688 12.07 3.33707 11.9993 3.2662C11.9286 3.19534 11.8889 3.09922 11.8889 2.99899V1.50352ZM3.54807 1.50352C3.54807 1.4033 3.58778 1.30718 3.65847 1.23631C3.72916 1.16544 3.82504 1.12563 3.92501 1.12563C4.02498 1.12563 4.12086 1.16544 4.19155 1.23631C4.26224 1.30718 4.30195 1.4033 4.30195 1.50352V2.99899C4.30195 3.09922 4.26224 3.19534 4.19155 3.2662C4.12086 3.33707 4.02498 3.37688 3.92501 3.37688C3.82504 3.37688 3.72916 3.33707 3.65847 3.2662C3.58778 3.19534 3.54807 3.09922 3.54807 2.99899V1.50352ZM14.8772 14.1717C14.8747 14.3573 14.8001 14.5345 14.6691 14.6658C14.5382 14.797 14.3614 14.8719 14.1762 14.8744H1.82536C1.63995 14.8723 1.4627 14.7976 1.33144 14.6663C1.20018 14.5351 1.12531 14.3575 1.12281 14.1717V6.59296H14.8772V14.1717Z' fill='%23167CAC'/%3E%3Cpath d='M2.33063 11.282H3.93464V12.6006C3.93464 12.7499 3.99379 12.8931 4.09907 12.9986C4.20435 13.1041 4.34715 13.1634 4.49604 13.1634C4.64494 13.1634 4.78773 13.1041 4.89301 12.9986C4.9983 12.8931 5.05745 12.7499 5.05745 12.6006V11.282H7.46346V12.6006C7.46346 12.7499 7.52261 12.8931 7.62789 12.9986C7.73318 13.1041 7.87597 13.1634 8.02486 13.1634C8.17376 13.1634 8.31655 13.1041 8.42184 12.9986C8.52712 12.8931 8.58627 12.7499 8.58627 12.6006V11.282H10.9923V12.6006C10.9923 12.7499 11.0514 12.8931 11.1567 12.9986C11.262 13.1041 11.4048 13.1634 11.5537 13.1634C11.7026 13.1634 11.8454 13.1041 11.9507 12.9986C12.0559 12.8931 12.1151 12.7499 12.1151 12.6006V11.282H13.7191C13.868 11.282 14.0108 11.2227 14.1161 11.1172C14.2214 11.0116 14.2805 10.8685 14.2805 10.7192C14.2805 10.57 14.2214 10.4268 14.1161 10.3213C14.0108 10.2157 13.868 10.1564 13.7191 10.1564H12.1151V8.84425C12.1151 8.69498 12.0559 8.55183 11.9507 8.44628C11.8454 8.34073 11.7026 8.28143 11.5537 8.28143C11.4048 8.28143 11.262 8.34073 11.1567 8.44628C11.0514 8.55183 10.9923 8.69498 10.9923 8.84425V10.1628H8.58627V8.84425C8.58627 8.69498 8.52712 8.55183 8.42184 8.44628C8.31655 8.34073 8.17376 8.28143 8.02486 8.28143C7.87597 8.28143 7.73318 8.34073 7.62789 8.44628C7.52261 8.55183 7.46346 8.69498 7.46346 8.84425V10.1628H5.05745V8.84425C5.05745 8.69498 4.9983 8.55183 4.89301 8.44628C4.78773 8.34073 4.64494 8.28143 4.49604 8.28143C4.34715 8.28143 4.20435 8.34073 4.09907 8.44628C3.99379 8.55183 3.93464 8.69498 3.93464 8.84425V10.1628H2.33063C2.18174 10.1628 2.03894 10.2221 1.93366 10.3277C1.82837 10.4332 1.76923 10.5764 1.76923 10.7257C1.76923 10.8749 1.82837 11.0181 1.93366 11.1236C2.03894 11.2292 2.18174 11.2885 2.33063 11.2885V11.282Z' fill='%23167CAC'/%3E%3C/svg%3E");
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: 1px;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.1762 1.52764H13.7656V1.50352C13.7656 1.10476 13.6076 0.722334 13.3263 0.44037C13.0451 0.158406 12.6636 0 12.2659 0C11.8681 0 11.4866 0.158406 11.2054 0.44037C10.9241 0.722334 10.7661 1.10476 10.7661 1.50352V1.52764H5.42476V1.50352C5.42476 1.10476 5.26675 0.722334 4.9855 0.44037C4.70424 0.158406 4.32277 0 3.92501 0C3.52725 0 3.14579 0.158406 2.86453 0.44037C2.58327 0.722334 2.42526 1.10476 2.42526 1.50352V1.52764H1.82376C1.34046 1.52891 0.877316 1.72195 0.53557 2.06455C0.193824 2.40716 0.00127018 2.87146 0 3.35598V14.1717C0.0016909 14.656 0.194379 15.1201 0.536035 15.4626C0.87769 15.8051 1.34059 15.9983 1.82376 16H14.1746C14.6581 15.9987 15.1214 15.8057 15.4634 15.4632C15.8054 15.1206 15.9983 14.6563 16 14.1717V3.35598C15.9987 2.87146 15.8062 2.40716 15.4644 2.06455C15.1227 1.72195 14.6595 1.52891 14.1762 1.52764ZM11.8889 1.50352C11.8889 1.4033 11.9286 1.30718 11.9993 1.23631C12.07 1.16544 12.1659 1.12563 12.2659 1.12563C12.3658 1.12563 12.4617 1.16544 12.5324 1.23631C12.6031 1.30718 12.6428 1.4033 12.6428 1.50352V2.99899C12.6428 3.09922 12.6031 3.19534 12.5324 3.2662C12.4617 3.33707 12.3658 3.37688 12.2659 3.37688C12.1659 3.37688 12.07 3.33707 11.9993 3.2662C11.9286 3.19534 11.8889 3.09922 11.8889 2.99899V1.50352ZM3.54807 1.50352C3.54807 1.4033 3.58778 1.30718 3.65847 1.23631C3.72916 1.16544 3.82504 1.12563 3.92501 1.12563C4.02498 1.12563 4.12086 1.16544 4.19155 1.23631C4.26224 1.30718 4.30195 1.4033 4.30195 1.50352V2.99899C4.30195 3.09922 4.26224 3.19534 4.19155 3.2662C4.12086 3.33707 4.02498 3.37688 3.92501 3.37688C3.82504 3.37688 3.72916 3.33707 3.65847 3.2662C3.58778 3.19534 3.54807 3.09922 3.54807 2.99899V1.50352ZM14.8772 14.1717C14.8747 14.3573 14.8001 14.5345 14.6691 14.6658C14.5382 14.797 14.3614 14.8719 14.1762 14.8744H1.82536C1.63995 14.8723 1.4627 14.7976 1.33144 14.6663C1.20018 14.5351 1.12531 14.3575 1.12281 14.1717V6.59296H14.8772V14.1717Z' fill='%23167CAC'/%3E%3Cpath d='M2.33063 11.282H3.93464V12.6006C3.93464 12.7499 3.99379 12.8931 4.09907 12.9986C4.20435 13.1041 4.34715 13.1634 4.49604 13.1634C4.64494 13.1634 4.78773 13.1041 4.89301 12.9986C4.9983 12.8931 5.05745 12.7499 5.05745 12.6006V11.282H7.46346V12.6006C7.46346 12.7499 7.52261 12.8931 7.62789 12.9986C7.73318 13.1041 7.87597 13.1634 8.02486 13.1634C8.17376 13.1634 8.31655 13.1041 8.42184 12.9986C8.52712 12.8931 8.58627 12.7499 8.58627 12.6006V11.282H10.9923V12.6006C10.9923 12.7499 11.0514 12.8931 11.1567 12.9986C11.262 13.1041 11.4048 13.1634 11.5537 13.1634C11.7026 13.1634 11.8454 13.1041 11.9507 12.9986C12.0559 12.8931 12.1151 12.7499 12.1151 12.6006V11.282H13.7191C13.868 11.282 14.0108 11.2227 14.1161 11.1172C14.2214 11.0116 14.2805 10.8685 14.2805 10.7192C14.2805 10.57 14.2214 10.4268 14.1161 10.3213C14.0108 10.2157 13.868 10.1564 13.7191 10.1564H12.1151V8.84425C12.1151 8.69498 12.0559 8.55183 11.9507 8.44628C11.8454 8.34073 11.7026 8.28143 11.5537 8.28143C11.4048 8.28143 11.262 8.34073 11.1567 8.44628C11.0514 8.55183 10.9923 8.69498 10.9923 8.84425V10.1628H8.58627V8.84425C8.58627 8.69498 8.52712 8.55183 8.42184 8.44628C8.31655 8.34073 8.17376 8.28143 8.02486 8.28143C7.87597 8.28143 7.73318 8.34073 7.62789 8.44628C7.52261 8.55183 7.46346 8.69498 7.46346 8.84425V10.1628H5.05745V8.84425C5.05745 8.69498 4.9983 8.55183 4.89301 8.44628C4.78773 8.34073 4.64494 8.28143 4.49604 8.28143C4.34715 8.28143 4.20435 8.34073 4.09907 8.44628C3.99379 8.55183 3.93464 8.69498 3.93464 8.84425V10.1628H2.33063C2.18174 10.1628 2.03894 10.2221 1.93366 10.3277C1.82837 10.4332 1.76923 10.5764 1.76923 10.7257C1.76923 10.8749 1.82837 11.0181 1.93366 11.1236C2.03894 11.2292 2.18174 11.2885 2.33063 11.2885V11.282Z' fill='%23167CAC'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
width: 16px; // check this
|
||||
height: 16px;
|
||||
display: flex;
|
||||
border-radius: 0 7px 7px 0;
|
||||
background-color: #E4F1F7;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
background-position: center;
|
||||
width: 16px; // check this
|
||||
height: 16px;
|
||||
display: flex;
|
||||
border-radius: 0 7px 7px 0;
|
||||
background-color: #e4f1f7;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
padding: 15px 0;
|
||||
min-width: 48px;
|
||||
}
|
||||
|
|
@ -225,16 +233,16 @@ input[type="date"]::-webkit-calendar-picker-indicator {
|
|||
color: $black;
|
||||
font-weight: 500;
|
||||
}
|
||||
span{
|
||||
font-weight:400;
|
||||
font-size:14px;
|
||||
color: #4D5151;
|
||||
}
|
||||
.form-test-error span{
|
||||
span {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: #4d5151;
|
||||
}
|
||||
.form-test-error span {
|
||||
color: #d4281c;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
position: relative;
|
||||
|
|
@ -259,7 +267,7 @@ input[type="date"]::-webkit-calendar-picker-indicator {
|
|||
display: flex;
|
||||
}
|
||||
}
|
||||
&.has-select-icon {
|
||||
&.has-select-icon {
|
||||
button[type="submit"] {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
|
|
@ -275,7 +283,7 @@ input[type="date"]::-webkit-calendar-picker-indicator {
|
|||
border-left: none;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
input {
|
||||
&.has-icon {
|
||||
|
|
|
|||
47
src/helpers/global-rule-definer.js
Normal file
47
src/helpers/global-rule-definer.js
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { defineRule } from 'vee-validate';
|
||||
import { errorMessages } from '@/constants/error-messages';
|
||||
import globalRules from '@/constants/global-rules';
|
||||
import { required, regex } from '@/helpers/validation-rules';
|
||||
|
||||
/**
|
||||
* @summary Define global rules related to names
|
||||
*/
|
||||
function defineGlobalNameRules() {
|
||||
defineRule(globalRules.FIRST_NAME_REQUIRED, required(errorMessages.FIRST_NAME_REQUIRED));
|
||||
defineRule(globalRules.LAST_NAME_REQUIRED, required(errorMessages.LAST_NAME_REQUIRED));
|
||||
defineRule(globalRules.POLICYHOLDER_FIRST_NAME_REQUIRED,
|
||||
required(errorMessages.POLICYHOLDER_FIRST_NAME_REQUIRED));
|
||||
defineRule(globalRules.POLICYHOLDER_LAST_NAME_REQUIRED,
|
||||
required(errorMessages.POLICYHOLDER_LAST_NAME_REQUIRED));
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Define global rules related to email addresses
|
||||
*/
|
||||
function defineGlobalEmailRules() {
|
||||
defineRule(globalRules.EMAIL_ADDRESS_REQUIRED, required(errorMessages.EMAIL_ADDRESS_REQUIRED));
|
||||
defineRule(globalRules.EMAIL_ADDRESS_FORMAT,
|
||||
regex(/^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9_\-.]+)\.([a-zA-Z]{2,})$/,
|
||||
errorMessages.EMAIL_ADDRESS_FORMAT));
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Define global rules related to phone numbers
|
||||
*/
|
||||
function defineGlobalPhoneNumberRules() {
|
||||
defineRule(globalRules.PHONE_NUMBER_REQUIRED, required(errorMessages.PHONE_NUMBER_REQUIRED));
|
||||
defineRule(globalRules.PHONE_NUMBER_FORMAT,
|
||||
regex(/^(\([0-9]{3}\)|[0-9]{3}) *[-.]? *[0-9]{3} *[-.]? *[0-9]{4}$/,
|
||||
errorMessages.PHONE_NUMBER_FORMAT));
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Define all global rules
|
||||
*/
|
||||
export default function defineGlobalRules() {
|
||||
defineGlobalNameRules();
|
||||
defineGlobalEmailRules();
|
||||
defineGlobalPhoneNumberRules();
|
||||
|
||||
defineRule(globalRules.OPTION_REQUIRED, required(errorMessages.OPTION_REQUIRED));
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
ref="firstName"
|
||||
inputId="08497a2efd9a4a73a70360ab47b4838d"
|
||||
disableAutoFill
|
||||
validationRules="first-name-required" />
|
||||
:validationRules="rules.firstName" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
|
|
@ -19,21 +19,15 @@
|
|||
ref="lastName"
|
||||
inputId="0030e56a57e74a4ab92de7fb8e97fec5"
|
||||
disableAutoFill
|
||||
validationRules="last-name-required" />
|
||||
:validationRules="rules.lastName" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import addressQuestions from "@/iss-components/address-questions/address-questions";
|
||||
import textboxQuestion from "@/digital-components/textbox-question/textbox-question";
|
||||
import { defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("first-name-required", required(errorMessages.FIRST_NAME_REQUIRED));
|
||||
defineRule("last-name-required", required(errorMessages.LAST_NAME_REQUIRED));
|
||||
import addressQuestions from '@/iss-components/address-questions/address-questions.vue';
|
||||
import textboxQuestion from '@/digital-components/textbox-question/textbox-question.vue';
|
||||
import globalRules from '@/constants/global-rules';
|
||||
|
||||
export default {
|
||||
name: "customer-questions",
|
||||
|
|
@ -56,6 +50,14 @@ export default {
|
|||
},
|
||||
validationRules: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rules: {
|
||||
firstName: globalRules.FIRST_NAME_REQUIRED,
|
||||
lastName: globalRules.LAST_NAME_REQUIRED
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
customerModel: {
|
||||
get: function () {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
:alertFewMoreQuestionsHeader="AlertFewMoreQuestionsHeader"
|
||||
:alertFewMoreQuestionsCopy="AlertFewMoreQuestionsCopy"
|
||||
:questionsData="questionsData"
|
||||
validationRules="questions-required"
|
||||
:validationRules="rules.optionRequired"
|
||||
v-model="selectedAnswers"
|
||||
@forwardButtonAction="forwardButtonAction"
|
||||
@back-click="navigateBack"
|
||||
|
|
@ -26,14 +26,11 @@ import { settleAllPromises } from '@/helpers/layout-helper';
|
|||
// Import Component
|
||||
import baseFormMixin from '@/mixins/base-form-mixin';
|
||||
import { issPageValues } from "@/router/router-constants/issPage-values";
|
||||
import { Form, defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { Form } from "vee-validate";
|
||||
import { useMainStore } from "@/store";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
import globalRules from '@/constants/global-rules';
|
||||
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
|
||||
import questionsPageLayout from "@/iss-components/questions-page-layout/questions-page-layout.vue";
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("questions-required", required(errorMessages.OPTION_REQUIRED));
|
||||
|
||||
export default {
|
||||
name: 'capability-questions',
|
||||
|
|
@ -45,8 +42,11 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
questionsData: [],
|
||||
selectedAnswers: {},
|
||||
currentGlassIndex: 0,
|
||||
selectedAnswers: {},
|
||||
currentGlassIndex: 0,
|
||||
rules: {
|
||||
optionRequired: globalRules.OPTION_REQUIRED
|
||||
}
|
||||
};
|
||||
},
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
|
|
|
|||
307
src/layouts/contact-details/contact-details.spec.js
Normal file
307
src/layouts/contact-details/contact-details.spec.js
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
// Components
|
||||
import contactDetails from '@/layouts/contact-details/contact-details';
|
||||
|
||||
// Supporting Files
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
import { getRandomString, getRandomInt, getRandomBoolean } from '@/helpers/data-generation.js';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import { navigationScenarios } from '@/router/router-constants/navigation-scenarios.js';
|
||||
import { useMainStore } from '@/store/index.js';
|
||||
|
||||
describe('contactDetails.vue', () => {
|
||||
describe('Rendering', () => {
|
||||
test('Should render site header', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(contactDetails, getMountOptions());
|
||||
|
||||
// Act
|
||||
const siteHeader = wrapper.findComponent({ ref: 'siteHeader' });
|
||||
|
||||
// Assert
|
||||
expect(siteHeader.exists()).toBe(true);
|
||||
});
|
||||
test('Should render sub title', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(contactDetails, getMountOptions());
|
||||
|
||||
// Act
|
||||
const siteSubHeader = wrapper.findComponent({ ref: 'siteSubHeader' });
|
||||
|
||||
// Assert
|
||||
expect(siteSubHeader.exists()).toBe(true);
|
||||
});
|
||||
test('Should render first name question subcomponent', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(contactDetails, getMountOptions());
|
||||
|
||||
// Act
|
||||
const firstNameQuestion = wrapper.findComponent({ ref: 'firstNameQuestion' });
|
||||
|
||||
// Assert
|
||||
expect(firstNameQuestion.exists()).toBe(true);
|
||||
});
|
||||
test('Should render last name question subcomponent', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(contactDetails, getMountOptions());
|
||||
|
||||
// Act
|
||||
const lastNameQuestion = wrapper.findComponent({ ref: 'lastNameQuestion' });
|
||||
|
||||
// Assert
|
||||
expect(lastNameQuestion.exists()).toBe(true);
|
||||
});
|
||||
test('Should render email question subcomponent', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(contactDetails, getMountOptions());
|
||||
|
||||
// Act
|
||||
const emailQuestion = wrapper.findComponent({ ref: 'emailQuestion' });
|
||||
|
||||
// Assert
|
||||
expect(emailQuestion.exists()).toBe(true);
|
||||
});
|
||||
test('Should render phone number question subcomponent', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(contactDetails, getMountOptions());
|
||||
|
||||
// Act
|
||||
const phoneNumberQuestion = wrapper.findComponent({ ref: 'phoneNumberQuestion' });
|
||||
|
||||
// Assert
|
||||
expect(phoneNumberQuestion.exists()).toBe(true);
|
||||
});
|
||||
test('Should render text updates checkbox subcomponent', () => {
|
||||
// Arrange
|
||||
const checkboxLabel = getRandomString(50, 100);
|
||||
const mockMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn().mockImplementation(() =>
|
||||
checkboxLabel),
|
||||
setCmsContent: jest.fn()
|
||||
}
|
||||
};
|
||||
const mountOptions = getMountOptions();
|
||||
mountOptions.mixins = [mockMixin];
|
||||
const wrapper = shallowMount(contactDetails, mountOptions);
|
||||
|
||||
// Act
|
||||
const textUpdatesCheckbox = wrapper.findComponent({ ref: 'requestTextUpdatesCheckbox' });
|
||||
|
||||
// Assert
|
||||
expect(textUpdatesCheckbox.exists()).toBe(true);
|
||||
expect(wrapper.vm.requestTextUpdatesCheckboxText).toBe(`${checkboxLabel}*`);
|
||||
});
|
||||
test('Should render technician notes textarea question subcomponent', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(contactDetails, getMountOptions());
|
||||
|
||||
// Act
|
||||
const notesQuestion = wrapper.findComponent({ ref: 'notesQuestion' });
|
||||
|
||||
// Assert
|
||||
expect(notesQuestion.exists()).toBe(true);
|
||||
});
|
||||
test('Should render disclaimer text', () => {
|
||||
// Arrange
|
||||
const disclaimerText = getRandomString(50, 100);
|
||||
const mockMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn().mockImplementation(() =>
|
||||
disclaimerText),
|
||||
setCmsContent: jest.fn()
|
||||
}
|
||||
};
|
||||
const mountOptions = getMountOptions();
|
||||
mountOptions.mixins = [mockMixin];
|
||||
const wrapper = shallowMount(contactDetails, mountOptions);
|
||||
const expectedDisclaimerText = `*${disclaimerText} I also agree to Safelite's`;
|
||||
|
||||
// Act
|
||||
const componentText = wrapper.text();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.textUpdateDisclaimerText).toBe(`*${disclaimerText}`);
|
||||
expect(componentText).toContain(expectedDisclaimerText);
|
||||
});
|
||||
test('Should render privacy policy link', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(contactDetails, getMountOptions());
|
||||
|
||||
// Act
|
||||
const privacyPolicyLink = wrapper.findComponent({ ref: 'privacyPolicyLink' });
|
||||
|
||||
// Assert
|
||||
expect(privacyPolicyLink.exists()).toBe(true);
|
||||
});
|
||||
test('Should render terms of use link', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(contactDetails, getMountOptions());
|
||||
|
||||
// Act
|
||||
const termsOfUseLink = wrapper.findComponent({ ref: 'termsOfUseLink' });
|
||||
|
||||
// Assert
|
||||
expect(termsOfUseLink.exists()).toBe(true);
|
||||
});
|
||||
test('Should render site footer', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(contactDetails, getMountOptions());
|
||||
|
||||
// Act
|
||||
const footer = wrapper.findComponent({ ref: 'siteFooter' });
|
||||
|
||||
// Assert
|
||||
expect(footer.exists()).toBe(true);
|
||||
});
|
||||
test('Mocked store with no contact info yields expected data', () => {
|
||||
// Arrange
|
||||
const mountOptions = getMountOptions();
|
||||
|
||||
const firstName = getRandomString(4, 15);
|
||||
const lastName = getRandomString(4, 15);
|
||||
const emailAddress = getRandomString(10, 20);
|
||||
const phoneNumber = getRandomInt(1000000000, 9999999999);
|
||||
const mainInitialState = {
|
||||
order: {
|
||||
customer: {
|
||||
firstName,
|
||||
lastName,
|
||||
emailAddress,
|
||||
phoneNumber
|
||||
}
|
||||
}
|
||||
};
|
||||
mountOptions.global = {
|
||||
plugins: [createTestingPinia({
|
||||
initialState: {
|
||||
main: mainInitialState
|
||||
}
|
||||
})]
|
||||
};
|
||||
|
||||
const wrapper = shallowMount(contactDetails, mountOptions);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.firstName).toBe(firstName);
|
||||
expect(wrapper.vm.lastName).toBe(lastName);
|
||||
expect(wrapper.vm.emailAddress).toBe(emailAddress);
|
||||
expect(wrapper.vm.phoneNumber).toBe(phoneNumber);
|
||||
});
|
||||
test('Mock store with contact info yields expected data', () => {
|
||||
// Arrange
|
||||
const customer = {
|
||||
firstName: getRandomString(4, 15),
|
||||
lastName: getRandomString(4, 15),
|
||||
emailAddress: getRandomString(10, 20),
|
||||
phoneNumber: getRandomInt(1000000000, 9999999999)
|
||||
};
|
||||
const contactInfo = {
|
||||
firstName: getRandomString(4, 15),
|
||||
lastName: getRandomString(4, 15),
|
||||
emailAddress: getRandomString(10, 20),
|
||||
phoneNumber: getRandomInt(1000000000, 9999999999),
|
||||
requestTextUpdates: getRandomBoolean(),
|
||||
notesForTechnician: getRandomString(50, 100)
|
||||
};
|
||||
const mainInitialState = {
|
||||
order: {
|
||||
customer,
|
||||
contactInfo
|
||||
}
|
||||
};
|
||||
const mountOptions = getMountOptions();
|
||||
mountOptions.global = {
|
||||
plugins: [createTestingPinia({
|
||||
initialState: {
|
||||
main: mainInitialState
|
||||
}
|
||||
})]
|
||||
};
|
||||
|
||||
const wrapper = shallowMount(contactDetails, mountOptions);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.firstName).toBe(contactInfo.firstName);
|
||||
expect(wrapper.vm.lastName).toBe(contactInfo.lastName);
|
||||
expect(wrapper.vm.emailAddress).toBe(contactInfo.emailAddress);
|
||||
expect(wrapper.vm.phoneNumber).toBe(contactInfo.phoneNumber);
|
||||
expect(wrapper.vm.requestTextUpdates).toBe(contactInfo.requestTextUpdates);
|
||||
expect(wrapper.vm.notesForTechnician).toBe(contactInfo.notesForTechnician);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Navigation', () => {
|
||||
test('Back button clicked triggers navigation', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(contactDetails, getMountOptions({
|
||||
router: {
|
||||
navigate: jest.fn()
|
||||
}
|
||||
}));
|
||||
|
||||
// Act
|
||||
wrapper.vm.backButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
||||
expect(wrapper.vm.$router.navigate)
|
||||
.toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK, undefined);
|
||||
});
|
||||
test('Forward button clicked triggers navigation', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(contactDetails, getMountOptions({
|
||||
router: {
|
||||
navigate: jest.fn()
|
||||
}
|
||||
}));
|
||||
|
||||
// Act
|
||||
wrapper.vm.forwardButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
||||
expect(wrapper.vm.$router.navigate)
|
||||
.toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD, undefined);
|
||||
});
|
||||
test('Forward button click updates contact info', () => {
|
||||
// Arrange
|
||||
const mountOptions = getMountOptions({
|
||||
router: {
|
||||
navigate: jest.fn()
|
||||
},
|
||||
global: {
|
||||
plugins: [createTestingPinia()]
|
||||
}
|
||||
});
|
||||
const wrapper = shallowMount(contactDetails, mountOptions);
|
||||
const firstName = getRandomString(4, 15);
|
||||
const lastName = getRandomString(4, 15);
|
||||
const emailAddress = getRandomString(10, 20);
|
||||
const phoneNumber = getRandomInt(1000000000, 9999999999);
|
||||
const requestTextUpdates = getRandomBoolean();
|
||||
const notesForTechnician = getRandomString(1, 100);
|
||||
wrapper.setData({
|
||||
firstName,
|
||||
lastName,
|
||||
emailAddress,
|
||||
phoneNumber,
|
||||
requestTextUpdates,
|
||||
notesForTechnician
|
||||
});
|
||||
|
||||
// Act
|
||||
wrapper.vm.forwardButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(useMainStore().updateContactInfo).toHaveBeenCalledWith({
|
||||
firstName,
|
||||
lastName,
|
||||
emailAddress,
|
||||
phoneNumber,
|
||||
requestTextUpdates,
|
||||
notesForTechnician
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,17 +1,97 @@
|
|||
<template>
|
||||
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }" >
|
||||
<Form
|
||||
ref="contact-details-form"
|
||||
v-slot="{ meta }"
|
||||
@submit="onSubmit"
|
||||
@invalidSubmit="onInvalidSubmit">
|
||||
<div class="page-container-grouped-styles">
|
||||
<div class="fade-on-route-transition position-relative">
|
||||
<siteHeader cmsWidgetName="SiteHeaderWidget"/>
|
||||
<siteHeader
|
||||
ref="siteHeader"
|
||||
:cmsWidgetName="widget.siteHeader" />
|
||||
<div class="container-fluid pb-2">
|
||||
<p>Placeholder for contact details page</p>
|
||||
<siteSubHeader
|
||||
id="sub-header"
|
||||
ref="siteSubHeader"
|
||||
class="margin-top-16"
|
||||
:cmsWidgetName="widget.siteSubHeader" />
|
||||
<textboxQuestion
|
||||
ref="firstNameQuestion"
|
||||
v-model="firstName"
|
||||
class="margin-top-24"
|
||||
inputId="firstName"
|
||||
:cmsWidgetName="widget.firstNameQuestion"
|
||||
isRequired
|
||||
:validationRules="rules.firstName" />
|
||||
<textboxQuestion
|
||||
ref="lastNameQuestion"
|
||||
v-model="lastName"
|
||||
class="margin-top-16"
|
||||
inputId="lastName"
|
||||
:cmsWidgetName="widget.lastNameQuestion"
|
||||
isRequired
|
||||
:validationRules="rules.lastName" />
|
||||
<textboxQuestion
|
||||
ref="emailQuestion"
|
||||
v-model="emailAddress"
|
||||
class="margin-top-16"
|
||||
inputId="emailAddress"
|
||||
:cmsWidgetName="widget.emailQuestion"
|
||||
isRequired
|
||||
:validationRules="rules.emailAddress" />
|
||||
<textboxQuestion
|
||||
ref="phoneNumberQuestion"
|
||||
v-model="phoneNumber"
|
||||
class="margin-top-16"
|
||||
inputId="phoneNumber"
|
||||
:cmsWidgetName="widget.phoneNumberQuestion"
|
||||
isRequired
|
||||
:validationRules="rules.phoneNumber" />
|
||||
<checkbox
|
||||
ref="requestTextUpdatesCheckbox"
|
||||
v-model="requestTextUpdates"
|
||||
:isChecked="requestTextUpdates"
|
||||
class="margin-top-8"
|
||||
checkboxName="requestTextUpdates"
|
||||
buttonID="requestTextUpdates"
|
||||
:checkboxLabel="requestTextUpdatesCheckboxText" />
|
||||
|
||||
<textareaQuestion
|
||||
ref="notesQuestion"
|
||||
v-model="notesForTechnician"
|
||||
class="margin-top-16"
|
||||
inputId="technicianNotes"
|
||||
:isDisabled="false"
|
||||
:isRequired="false"
|
||||
:cmsWidgetName="widget.notesQuestion"
|
||||
maxLength="250"
|
||||
:inputRows="4" />
|
||||
|
||||
<p
|
||||
ref="disclaimerText"
|
||||
class="disclaimer margin-top-24">
|
||||
{{ textUpdateDisclaimerText }} I also agree to Safelite's
|
||||
<textLink
|
||||
ref="privacyPolicyLink"
|
||||
class="disclaimer-link"
|
||||
linkType="text"
|
||||
text="Privacy Policy"
|
||||
href="//www.safelite.com/privacy-center" />
|
||||
and
|
||||
<textLink
|
||||
ref="termsOfUseLink"
|
||||
class="disclaimer-link"
|
||||
linkType="text"
|
||||
text="Terms of Use"
|
||||
href="//www.safelite.com/terms-of-use" />.
|
||||
</p>
|
||||
|
||||
<siteFooter
|
||||
cmsWidgetName="SiteFooterWidget"
|
||||
ref="siteFooter"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
@ForwardClicked="navigateForward"
|
||||
@back-clicked="backButtonAction"
|
||||
/>
|
||||
ref="siteFooter"
|
||||
:cmsWidgetName="widget.siteFooter"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
@forwardClicked="forwardButtonAction"
|
||||
@backClicked="backButtonAction" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -20,66 +100,145 @@
|
|||
<script>
|
||||
// Components
|
||||
import siteHeader from '@/iss-components/site-header/site-header';
|
||||
import siteFooter from "@/iss-components/site-footer/site-footer";
|
||||
import siteFooter from '@/iss-components/site-footer/site-footer';
|
||||
import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header';
|
||||
import textboxQuestion from '@/digital-components/textbox-question/textbox-question';
|
||||
import checkbox from '@/ux-components/checkbox/checkbox';
|
||||
import textareaQuestion from '@/digital-components/textarea-question/textarea-question';
|
||||
import textLink from '@/ux-components/text-link/text-link';
|
||||
|
||||
// Supporting files
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import { Form } from "vee-validate";
|
||||
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper.js';
|
||||
import { Form } from 'vee-validate';
|
||||
import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
||||
import { useMainStore } from '@/store';
|
||||
import { useMainStore } from '@/store/index.js';
|
||||
import globalRules from '@/constants/global-rules.js';
|
||||
|
||||
export default {
|
||||
name: "contact-details",
|
||||
name: 'contact-details',
|
||||
components: {
|
||||
siteHeader,
|
||||
siteSubHeader,
|
||||
textboxQuestion,
|
||||
checkbox,
|
||||
textareaQuestion,
|
||||
siteFooter,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form,
|
||||
textLink
|
||||
},
|
||||
mixins: [BaseFormMixin],
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
const cmsContent = await fetchCmsContentForPage(to.query.issPage);
|
||||
|
||||
next((vm) => {
|
||||
vm.setCmsContent(cmsContent);
|
||||
});
|
||||
},
|
||||
data() {
|
||||
|
||||
const { firstName,
|
||||
lastName,
|
||||
emailAddress,
|
||||
phoneNumber,
|
||||
requestTextUpdates,
|
||||
notesForTechnician } = useMainStore().contactInfo;
|
||||
return {
|
||||
firstName,
|
||||
lastName,
|
||||
emailAddress,
|
||||
phoneNumber,
|
||||
requestTextUpdates,
|
||||
notesForTechnician,
|
||||
widget: {
|
||||
siteHeader: 'SiteHeaderWidget',
|
||||
siteSubHeader: 'SiteSubHeaderWidget',
|
||||
firstNameQuestion: 'FirstNameQuestionWidget',
|
||||
lastNameQuestion: 'LastNameQuestionWidget',
|
||||
emailQuestion: 'EmailQuestionWidget',
|
||||
phoneNumberQuestion: 'PhoneNumberQuestionWidget',
|
||||
requestTextUpdates: 'TextContentWidget',
|
||||
notesQuestion: 'NotesQuestionWidget',
|
||||
disclaimer: 'TextUpdateDisclaimerWidget',
|
||||
siteFooter: 'SiteFooterWidget'
|
||||
},
|
||||
rules: {
|
||||
firstName: globalRules.FIRST_NAME_REQUIRED,
|
||||
lastName: globalRules.LAST_NAME_REQUIRED,
|
||||
emailAddress: `${globalRules.EMAIL_ADDRESS_REQUIRED}|${globalRules.EMAIL_ADDRESS_FORMAT}`,
|
||||
phoneNumber: `${globalRules.PHONE_NUMBER_REQUIRED}|${globalRules.PHONE_NUMBER_FORMAT}`
|
||||
}
|
||||
};
|
||||
},
|
||||
setup() {
|
||||
const mainStore = useMainStore();
|
||||
return { mainStore };
|
||||
computed: {
|
||||
/**
|
||||
* @summary Returns the CMS text associated with the "get text updates" checkbox.
|
||||
*/
|
||||
requestTextUpdatesCheckboxText() {
|
||||
return `${this.getCmsContent(this.widget.requestTextUpdates, 'Text')}*`;
|
||||
},
|
||||
/**
|
||||
* @summary Returns the CMS text associated with the "get text updates" checkbox.
|
||||
*/
|
||||
textUpdateDisclaimerText() {
|
||||
return `*${this.getCmsContent(this.widget.disclaimer, 'Text')}`;
|
||||
}
|
||||
},
|
||||
async beforeRouteEnter(to, from, next)
|
||||
methods:
|
||||
{
|
||||
// Call APIs
|
||||
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
|
||||
|
||||
// Settle promises and get results
|
||||
const promiseResultMap = [
|
||||
{
|
||||
resultKey: "cmsContent",
|
||||
promise: cmsContentPromise,
|
||||
},];
|
||||
|
||||
//use resultMap to populate layout content.
|
||||
let resultMap = await settleAllPromises(promiseResultMap);
|
||||
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
});
|
||||
},
|
||||
methods:
|
||||
{
|
||||
backButtonAction() {
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||
},
|
||||
|
||||
forwardButtonAction() {
|
||||
},
|
||||
|
||||
navigateForward() {
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD,
|
||||
this.$route
|
||||
);
|
||||
},
|
||||
},
|
||||
components: {
|
||||
siteHeader,
|
||||
siteFooter,
|
||||
Form,
|
||||
/**
|
||||
* @summary Steps to perform when back button clicked.
|
||||
*/
|
||||
backButtonAction() {
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||
},
|
||||
/**
|
||||
* @summary Steps to perform when forward button clicked.
|
||||
*/
|
||||
forwardButtonAction() {
|
||||
const contactInfo = {
|
||||
firstName: this.firstName,
|
||||
lastName: this.lastName,
|
||||
emailAddress: this.emailAddress,
|
||||
phoneNumber: this.phoneNumber,
|
||||
requestTextUpdates: this.requestTextUpdates,
|
||||
notesForTechnician: this.notesForTechnician
|
||||
};
|
||||
useMainStore().updateContactInfo(contactInfo);
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD,
|
||||
this.$route);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
|
||||
<style lang="scss" scoped>
|
||||
#sub-header {
|
||||
span {
|
||||
color: $black;
|
||||
}
|
||||
}
|
||||
|
||||
.margin-top-24 {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.margin-top-16 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.margin-top-8 {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.disclaimer {
|
||||
font-size: 12px;
|
||||
color: $darker-gray;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.disclaimer-link {
|
||||
text-decoration: none;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
:alertFewMoreQuestionsHeader="AlertFewMoreQuestionsHeader"
|
||||
:alertFewMoreQuestionsCopy="AlertFewMoreQuestionsCopy"
|
||||
:questionsData="questionsData"
|
||||
validationRules="questions-required"
|
||||
:validationRules="rules.optionRequired"
|
||||
v-model="selectedAnswers"
|
||||
@forwardButtonAction="forwardButtonAction"
|
||||
@back-click="navigateBack"
|
||||
|
|
@ -25,9 +25,7 @@
|
|||
// Import Supporting Files
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import { defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
import globalRules from '@/constants/global-rules';
|
||||
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
|
||||
import { useMainStore } from "@/store";
|
||||
import { issPageValues } from "@/router/router-constants/issPage-values";
|
||||
|
|
@ -37,9 +35,6 @@ import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
|||
import { Form } from "vee-validate";
|
||||
import questionsPageLayout from "@/iss-components/questions-page-layout/questions-page-layout.vue";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("questions-required", required(errorMessages.OPTION_REQUIRED));
|
||||
|
||||
export default {
|
||||
name: "molding-questions",
|
||||
mixins: [BaseFormMixin, vehicleQuestionsMixin],
|
||||
|
|
@ -65,6 +60,9 @@ export default {
|
|||
questionsData: [],
|
||||
selectedAnswers: {},
|
||||
currentGlassIndex: 0,
|
||||
rules: {
|
||||
optionRequired: globalRules.OPTION_REQUIRED
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
:alertFewMoreQuestionsHeader="AlertFewMoreQuestionsHeader"
|
||||
:alertFewMoreQuestionsCopy="AlertFewMoreQuestionsCopy"
|
||||
:questionsData="questionsData"
|
||||
validationRules="questions-required"
|
||||
:validationRules="rules.optionRequired"
|
||||
v-model="selectedAnswers"
|
||||
@forwardButtonAction="forwardButtonAction"
|
||||
@back-click="navigateBack"
|
||||
|
|
@ -23,14 +23,10 @@ import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
|||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import { useMainStore } from '@/store';
|
||||
import { issPageValues } from "@/router/router-constants/issPage-values";
|
||||
import { Form, defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
import { Form } from "vee-validate";
|
||||
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
|
||||
import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("questions-required", required(errorMessages.OPTION_REQUIRED));
|
||||
import globalRules from '@/constants/global-rules';
|
||||
|
||||
export default {
|
||||
name: "part-questions",
|
||||
|
|
@ -59,6 +55,9 @@ export default {
|
|||
questionsData: [],
|
||||
selectedAnswers: {},
|
||||
currentGlassIndex: 0,
|
||||
rules: {
|
||||
optionRequired: globalRules.OPTION_REQUIRED
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
isRequired
|
||||
ref="policyHolderFirstName"
|
||||
disableAutoFill
|
||||
validationRules="first-name-required" />
|
||||
:validationRules="rules.firstName" />
|
||||
<textboxQuestion
|
||||
class="mt-4"
|
||||
inputId="lastNameField"
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
isRequired
|
||||
ref="policyHolderLastName"
|
||||
disableAutoFill
|
||||
validationRules="last-name-required" />
|
||||
:validationRules="rules.lastName" />
|
||||
</div>
|
||||
</div>
|
||||
<siteFooter
|
||||
|
|
@ -51,16 +51,11 @@ import siteFooter from "@/iss-components/site-footer/site-footer";
|
|||
// Supporting files
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import { Form, defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
import { Form } from "vee-validate";
|
||||
import globalRules from '@/constants/global-rules';
|
||||
import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
||||
import { useMainStore } from '@/store';
|
||||
|
||||
//define validation rules
|
||||
defineRule("first-name-required", required(errorMessages.POLICYHOLDER_FIRST_NAME_REQUIRED));
|
||||
defineRule("last-name-required", required(errorMessages.POLICYHOLDER_LAST_NAME_REQUIRED));
|
||||
|
||||
export default {
|
||||
name: "policy-holder-details",
|
||||
mixins: [BaseFormMixin],
|
||||
|
|
@ -68,6 +63,10 @@ export default {
|
|||
return {
|
||||
customerQuestions: this.getPolicyHolderDetailsFromStore(),
|
||||
vehiclesFound: [],
|
||||
rules: {
|
||||
firstName: globalRules.POLICYHOLDER_FIRST_NAME_REQUIRED,
|
||||
lastName: globalRules.POLICYHOLDER_LAST_NAME_REQUIRED
|
||||
}
|
||||
};
|
||||
},
|
||||
setup() {
|
||||
|
|
|
|||
|
|
@ -1,47 +1,49 @@
|
|||
<template>
|
||||
<buttonQuestion
|
||||
:questionText="questionText"
|
||||
<buttonQuestion
|
||||
ref="policyVehiclesQuestion"
|
||||
buttonTypeString="listButton"
|
||||
isOverflowScrollable
|
||||
:answers="answers"
|
||||
v-model="selectedVehicleVin"
|
||||
:questionText="questionText"
|
||||
buttonTypeString="listButton"
|
||||
isOverflowScrollable
|
||||
:answers="answers"
|
||||
isRequired
|
||||
:validation-rules="validationRules" />
|
||||
:validationRules="validationRules" />
|
||||
</template>
|
||||
<script>
|
||||
import buttonQuestion from "@/digital-components/button-question/button-question";
|
||||
import buttonQuestion from '@/digital-components/button-question/button-question.vue';
|
||||
|
||||
export default ({
|
||||
name: "policy-vehicles-question",
|
||||
name: 'policy-vehicles-question',
|
||||
components: {
|
||||
buttonQuestion,
|
||||
buttonQuestion
|
||||
},
|
||||
props:{
|
||||
props: {
|
||||
vehicles: Array,
|
||||
validationRules: String,
|
||||
modelValue: String,
|
||||
validationRules: String,
|
||||
modelValue: String
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
computed: {
|
||||
questionText() {
|
||||
return this.getCmsContent("PolicyVehiclesQuestion", "QuestionText");
|
||||
questionText() {
|
||||
return this.getCmsContent('PolicyVehiclesQuestion', 'QuestionText');
|
||||
},
|
||||
answers(){
|
||||
const policyVehiclesAnswerfromCMS = this.getCmsContent("PolicyVehiclesQuestion", "Answers");
|
||||
const combinedVehicles = [...this.vehicles, ...policyVehiclesAnswerfromCMS];
|
||||
answers() {
|
||||
const policyVehiclesAnswerFromCMS = this.getCmsContent('PolicyVehiclesQuestion', 'Answers');
|
||||
const combinedVehicles = [...this.vehicles, ...policyVehiclesAnswerFromCMS];
|
||||
return combinedVehicles;
|
||||
},
|
||||
selectedVehicleVin: {
|
||||
get: function () {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set: function (newValue) {
|
||||
this.$emit("update:modelValue", newValue);
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
set(newValue) {
|
||||
this.$emit('update:modelValue', newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.question-text {
|
||||
margin-top: 1rem;
|
||||
& > span {
|
||||
|
|
@ -50,4 +52,4 @@ export default ({
|
|||
line-height: 2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,22 +1,35 @@
|
|||
<template>
|
||||
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }" >
|
||||
<Form
|
||||
ref="theForm"
|
||||
v-slot="{ meta }"
|
||||
@submit="onSubmit"
|
||||
@invalidSubmit="onInvalidSubmit">
|
||||
<div class="page-container-grouped-styles">
|
||||
<div class="fade-on-route-transition position-relative">
|
||||
<siteHeader class="mb-2 header" cmsWidgetName="SiteHeaderWidget"/>
|
||||
<siteHeader
|
||||
class="mb-2 header"
|
||||
cmsWidgetName="SiteHeaderWidget" />
|
||||
<div class="select-car">
|
||||
<div class="container-fluid pb-2 px-5">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="select-car-form rounded text-center">
|
||||
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage="displayGeneric" class="mt-2 mb-4" />
|
||||
<policyVehiclesQuestion
|
||||
class="px-4"
|
||||
cmsWidgetName="PolicyVehiclesQuestion"
|
||||
:vehicles="VehiclesForQuestions"
|
||||
validationRules="option-required"
|
||||
v-model="selectedVehicleVin"
|
||||
/>
|
||||
<siteFooter cmsWidgetName="SiteFooterWidget" ref="siteFooter" :isForwardActionDisabled="!meta.valid" @ForwardClicked="forwardButtonAction" @back-clicked="backButtonAction"/>
|
||||
<vehicleBanner
|
||||
cmsWidgetName="VehicleBannerWidget"
|
||||
:displayGenericVehicleImage="displayGeneric"
|
||||
class="mt-2 mb-4" />
|
||||
<policyVehiclesQuestion
|
||||
v-model="selectedVehicleVin"
|
||||
class="px-4"
|
||||
cmsWidgetName="PolicyVehiclesQuestion"
|
||||
:vehicles="VehiclesForQuestions"
|
||||
:validationRules="rules.optionRequired" />
|
||||
<siteFooter
|
||||
ref="siteFooter"
|
||||
cmsWidgetName="SiteFooterWidget"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
@ForwardClicked="forwardButtonAction"
|
||||
@backClicked="backButtonAction" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -29,69 +42,137 @@
|
|||
<script>
|
||||
// Components
|
||||
import siteHeader from '@/iss-components/site-header/site-header';
|
||||
import siteFooter from "@/iss-components/site-footer/site-footer";
|
||||
import vehicleBanner from "@/iss-components/vehicle-banner/vehicle-banner";
|
||||
import policyVehiclesQuestion from "@/layouts/policy-vehicles/policy-vehicles-question/policy-vehicles-question";
|
||||
import siteFooter from '@/iss-components/site-footer/site-footer';
|
||||
import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner';
|
||||
import policyVehiclesQuestion from '@/layouts/policy-vehicles/policy-vehicles-question/policy-vehicles-question';
|
||||
// Supporting files
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { Form, defineRule } from "vee-validate";
|
||||
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper.js';
|
||||
import { Form } from 'vee-validate';
|
||||
import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
||||
import { issPageValues } from "@/router/router-constants/issPage-values";
|
||||
import { useMainStore } from '@/store';
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
import { vehicleSelectionOptions } from "@/constants/vehicle-selection-options";
|
||||
import { endorsementOptions } from "@/constants/endorsement-options";
|
||||
import { issPageValues } from '@/router/router-constants/issPage-values.js';
|
||||
import { vehicleSelectionOptions } from '@/constants/vehicle-selection-options.js';
|
||||
import { endorsementOptions } from '@/constants/endorsement-options.js';
|
||||
import globalRules from '@/constants/global-rules.js';
|
||||
import { useMainStore } from '@/store/index.js';
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
||||
export default {
|
||||
name: "policy-vehicles",
|
||||
name: 'policy-vehicles',
|
||||
components: {
|
||||
siteHeader,
|
||||
siteFooter,
|
||||
vehicleBanner,
|
||||
policyVehiclesQuestion,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form
|
||||
},
|
||||
mixins: [BaseFormMixin],
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
const cmsContentPromise = await fetchCmsContentForPage(to.query.issPage);
|
||||
next((vm) => {
|
||||
vm.setCmsContent(cmsContentPromise);
|
||||
});
|
||||
},
|
||||
data() {
|
||||
const policyVehicles = useMainStore().pageData(issPageValues.POLICY_VEHICLES);
|
||||
return {
|
||||
policyVehicles: policyVehicles,
|
||||
selectedVehicleVin: (policyVehicles?.length ?? 0) == 1
|
||||
policyVehicles,
|
||||
selectedVehicleVin: (policyVehicles?.length ?? 0) === 1
|
||||
? policyVehicles[0].vin
|
||||
: "",
|
||||
displayGeneric: true,
|
||||
bailout: false
|
||||
: '',
|
||||
displayGeneric: (policyVehicles?.length ?? 0) !== 1,
|
||||
bailout: false,
|
||||
rules: {
|
||||
optionRequired: globalRules.OPTION_REQUIRED
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
VehiclesForQuestions() {
|
||||
// Map API result data, to address-vehicles data structure
|
||||
const vehicles = this.policyVehicles;
|
||||
const mappedData = vehicles?.map((v) => {
|
||||
const maskSymbol = 'X';
|
||||
const vinStart = maskSymbol.repeat(v.vin.length - 6);
|
||||
const vinEnd = v.vin.substring(v.vin.length - 6);
|
||||
return {
|
||||
vin: v.vin,
|
||||
vehicle: v,
|
||||
Text: `${v.vehicleYear} ${v.vehicleMake} ${v.vehicleModel}`,
|
||||
Name: v.vin,
|
||||
SubText: `VIN ${vinStart}${vinEnd}`
|
||||
};
|
||||
}) ?? [];
|
||||
return mappedData;
|
||||
},
|
||||
noCompensationForSelectedVehicle() {
|
||||
const vehicle = this.policyVehicles.find((policyVehicle) =>
|
||||
policyVehicle.vin === this.selectedVehicleVin);
|
||||
return (vehicle?.coverages?.length ?? 0) === 0;
|
||||
},
|
||||
deductibleForSelectedVehicle() {
|
||||
const vehicle = this.policyVehicles.find((policyVehicle) =>
|
||||
policyVehicle?.vin === this.selectedVehicleVin);
|
||||
if (!vehicle) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return vehicle.coverages?.length ?? false
|
||||
? vehicle?.coverages[0].deductible
|
||||
: 0;
|
||||
},
|
||||
repairWaivedForSelectedVehicle() {
|
||||
const vehicle = this.policyVehicles.find((policyVehicle) =>
|
||||
policyVehicle.vin === this.selectedVehicleVin);
|
||||
return vehicle?.endorsements?.includes(endorsementOptions.REPAIR_WAIVED) ?? false;
|
||||
},
|
||||
VehiclesFromApi() {
|
||||
return useMainStore().pageData(issPageValues.POLICY_VEHICLES);
|
||||
},
|
||||
selectedVehicle() {
|
||||
const vehicle = this.mainStore.lookupVehicleByVin(this.selectedVehicleVin);
|
||||
return vehicle;
|
||||
}
|
||||
},
|
||||
async beforeRouteEnter(to, from, next)
|
||||
{
|
||||
// Call APIs
|
||||
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
|
||||
// Settle promises and get results
|
||||
const promiseResultMap = [
|
||||
{
|
||||
resultKey: "cmsContent",
|
||||
promise: cmsContentPromise,
|
||||
},
|
||||
];
|
||||
//use resultMap to populate layout content.
|
||||
let resultMap = await settleAllPromises(promiseResultMap);
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
});
|
||||
watch: {
|
||||
async selectedVehicleVin(value) {
|
||||
if (value === vehicleSelectionOptions.VEHICLE_NOT_LISTED) {
|
||||
// clear previously selected vehicle and image
|
||||
this.mainStore.resetVehicleState();
|
||||
this.displayGeneric = true;
|
||||
} else {
|
||||
// get vehicle details from selected VIN
|
||||
const vehicle = await this.lookupVehicleByVin(value);
|
||||
|
||||
// handle error in case vehicle info doesn't come back for selected VIN
|
||||
if (vehicle?.error === true) {
|
||||
this.mainStore.resetVehicleState();
|
||||
this.displayGeneric = true;
|
||||
return;
|
||||
}
|
||||
if (vehicle) {
|
||||
// save selected vehicle to the store
|
||||
this.mainStore.updateVehicle(vehicle.data);
|
||||
this.displayGeneric = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods:
|
||||
methods:
|
||||
{
|
||||
backButtonAction() {
|
||||
backButtonAction() {
|
||||
useMainStore().issConfig.disabledFields.policyNumber = true;
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||
},
|
||||
async forwardButtonAction() {
|
||||
if (this.selectedVehicleVin != vehicleSelectionOptions.VEHICLE_NOT_LISTED){
|
||||
const vehicleLookupResponse = await this.lookupVehicleByVin(this.selectedVehicleVin);
|
||||
if (vehicleLookupResponse.error) {
|
||||
if (this.selectedVehicleVin !== vehicleSelectionOptions.VEHICLE_NOT_LISTED) {
|
||||
const vehicleLookupResponse
|
||||
= await this.lookupVehicleByVin(this.selectedVehicleVin);
|
||||
if (vehicleLookupResponse.error) {
|
||||
this.bailout = true;
|
||||
return this.navigateForward();
|
||||
return this.navigateForward();
|
||||
}
|
||||
|
||||
this.vehicleFromLookup = Object.assign(vehicleLookupResponse.data, {
|
||||
this.vehicleFromLookup = Object.assign(vehicleLookupResponse.data, {
|
||||
vin: this.selectedVehicleVin,
|
||||
noCompensation: this.noCompensationForSelectedVehicle,
|
||||
deductible: this.deductibleForSelectedVehicle,
|
||||
|
|
@ -103,128 +184,37 @@ export default {
|
|||
return this.navigateForward();
|
||||
},
|
||||
navigateForward() {
|
||||
if(this.bailout)
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
if (this.bailout) {
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
this.$route,
|
||||
{},
|
||||
{});
|
||||
} else if (this.selectedVehicleVin === vehicleSelectionOptions.VEHICLE_NOT_LISTED) {
|
||||
this.$router
|
||||
.navigate(this.navigationScenarios.CLICKED_FORWARD_NON_LISTED_VEHICLE,
|
||||
this.$route,
|
||||
{},
|
||||
{}
|
||||
);
|
||||
else if(this.selectedVehicleVin == vehicleSelectionOptions.VEHICLE_NOT_LISTED)
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD_NON_LISTED_VEHICLE,
|
||||
{});
|
||||
} else {
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_LISTED_VEHICLE,
|
||||
this.$route,
|
||||
{},
|
||||
{}
|
||||
);
|
||||
else
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD_LISTED_VEHICLE,
|
||||
this.$route,
|
||||
{},
|
||||
{}
|
||||
);
|
||||
{});
|
||||
}
|
||||
},
|
||||
async lookupVehicleByVin(vin) {
|
||||
try {
|
||||
return await useMainStore().lookupVehicleByVin(vin);
|
||||
}
|
||||
catch (responseError) {
|
||||
} catch (responseError) {
|
||||
return {
|
||||
error: true,
|
||||
error: true
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
computed:{
|
||||
VehiclesForQuestions() {
|
||||
// Map API result data, to address-vehicles data structure
|
||||
const vehicles = this.policyVehicles;
|
||||
const mappedData = vehicles?.map((v) => {
|
||||
const maskSymbol = "X";
|
||||
const vinStart = maskSymbol.repeat(v.vin.length - 6);
|
||||
const vinEnd = v.vin.substring(v.vin.length - 6);
|
||||
return {
|
||||
vin: v.vin,
|
||||
vehicle: v,
|
||||
Text: v.vehicleYear + " " + v.vehicleMake + " " + v.vehicleModel,
|
||||
Name: v.vin,
|
||||
SubText: "VIN " + vinStart + vinEnd,
|
||||
};
|
||||
}) ?? [];
|
||||
return mappedData;
|
||||
},
|
||||
noCompensationForSelectedVehicle() {
|
||||
const vehicle = this.policyVehicles.find((vehicle) => { return vehicle.vin == this.selectedVehicleVin; });
|
||||
return (vehicle?.coverages?.length ?? 0) == 0;
|
||||
},
|
||||
deductibleForSelectedVehicle() {
|
||||
const vehicle = this.policyVehicles.find((vehicle) => { return vehicle?.vin == this.selectedVehicleVin; });
|
||||
if (!vehicle){
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return vehicle.coverages?.length ?? false
|
||||
? vehicle?.coverages[0].deductible
|
||||
: 0;
|
||||
},
|
||||
repairWaivedForSelectedVehicle() {
|
||||
const vehicle = this.policyVehicles.find((vehicle) => { return vehicle.vin == this.selectedVehicleVin; });
|
||||
return vehicle?.endorsements?.includes(endorsementOptions.REPAIR_WAIVED) ?? false;
|
||||
},
|
||||
VehiclesFromApi() {
|
||||
return useMainStore().pageData(issPageValues.POLICY_VEHICLES);
|
||||
},
|
||||
selectedVehicle() {
|
||||
const vehicle = this.mainStore.lookupVehicleByVin(this.selectedVehicleVin);
|
||||
return vehicle;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
async selectedVehicleVin(value) {
|
||||
if (value === vehicleSelectionOptions.VEHICLE_NOT_LISTED) {
|
||||
// clear previously selected vehicle and image
|
||||
this.mainStore.resetVehicleState();
|
||||
this.displayGeneric = true;
|
||||
}
|
||||
|
||||
else {
|
||||
// get vehicle details from selected VIN
|
||||
const vehicle = await this.lookupVehicleByVin(value);
|
||||
|
||||
// handle error in case vehicle info doesn't come back for selected VIN
|
||||
if (vehicle?.error === true) {
|
||||
this.mainStore.resetVehicleState();
|
||||
this.displayGeneric = true;
|
||||
return;
|
||||
}
|
||||
if (vehicle) {
|
||||
// save selected vehicle to the store
|
||||
this.mainStore.updateVehicle(vehicle.data);
|
||||
this.displayGeneric = true;
|
||||
|
||||
// get the style(s) associated with the selected YMM
|
||||
const styleOptions = await this.mainStore.getVehicleStyles(
|
||||
vehicle.data.year,
|
||||
vehicle.data.make,
|
||||
vehicle.data.model,
|
||||
);
|
||||
// if there is more than 1 style for the selected vehicle, display generic/blurred image
|
||||
this.displayGeneric = styleOptions?.data?.length > 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
siteHeader,
|
||||
siteFooter,
|
||||
vehicleBanner,
|
||||
policyVehiclesQuestion,
|
||||
Form,
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.header{
|
||||
margin-bottom: 0rem !important;
|
||||
}
|
||||
|
|
@ -233,4 +223,4 @@ export default {
|
|||
margin-top: 0rem !important;
|
||||
line-height: 1.50rem;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
groupName="prefQuestions"
|
||||
buttonTypeString="providerPrefRadio"
|
||||
v-model="selectedProvider"
|
||||
validationRules="option-required"
|
||||
:validationRules="rules.optionRequired"
|
||||
isRequired
|
||||
class="mx-5" />
|
||||
<div class="px-5">
|
||||
|
|
@ -44,7 +44,6 @@
|
|||
// Import Supporting Files
|
||||
import { fetchCmsContentForPage, setupModalLinks, processIfStatements } from '@/helpers/cms-content-helper';
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
import buttonQuestion from "@/digital-components/button-question/button-question";
|
||||
import contentGroupModal from "@/iss-components/content-group-modal/content-group-modal";
|
||||
|
|
@ -52,7 +51,7 @@ import { issPageValues } from "@/router/router-constants/issPage-values";
|
|||
|
||||
// Import Component
|
||||
import baseFormMixin from "@/mixins/base-form-mixin";
|
||||
import { Form, defineRule } from "vee-validate";
|
||||
import { Form } from "vee-validate";
|
||||
import siteFooter from "@/iss-components/site-footer/site-footer";
|
||||
import siteHeader from "@/iss-components/site-header/site-header";
|
||||
import siteSubHeader from "@/iss-components/site-sub-header/site-sub-header";
|
||||
|
|
@ -61,9 +60,8 @@ import recalModal from "@/layouts/coverage-statement/recal-modal/recal-modal";
|
|||
import steeringModal from "@/layouts/provider-preference/steering-modal/steering-modal";
|
||||
import shopPreferenceModal from "@/layouts/provider-preference/shop-preference-modal/shop-preference-modal";
|
||||
import tpaRecalModal from "@/layouts/provider-preference/tpa-recal-modal/tpa-recal-modal.vue"
|
||||
import globalRules from '@/constants/global-rules';
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule('option-required', required(errorMessages.OPTION_REQUIRED));
|
||||
const options = { SAFELITE: 'SafeliteOption', TPA: 'TPAOption' };
|
||||
|
||||
export default {
|
||||
|
|
@ -88,6 +86,9 @@ export default {
|
|||
selectedProvider : null,
|
||||
showSteeringLink: false,
|
||||
tpaAcknowledgement: false,
|
||||
rules: {
|
||||
optionRequired: globalRules.OPTION_REQUIRED
|
||||
}
|
||||
};
|
||||
},
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<checkBox
|
||||
class="mb-2"
|
||||
:class="showError && ' has-error'"
|
||||
validationRules="option-required"
|
||||
:validationRules="rules.optionRequired"
|
||||
checkboxName="tpaAcknowledgement"
|
||||
buttonID="tpaAcknowledgement"
|
||||
:tabIndex="0"
|
||||
|
|
@ -35,6 +35,7 @@
|
|||
import modal from "@/digital-components/modal/modal";
|
||||
import tpaRecalToggle from "@/layouts/provider-preference/tpa-recal-modal/tpa-recal-toggle/tpa-recal-toggle.vue"
|
||||
import checkBox from "@/ux-components/checkbox/checkbox.vue"
|
||||
import globalRules from "@/constants/global-rules";
|
||||
|
||||
export default {
|
||||
name: "content-group-modal",
|
||||
|
|
@ -45,7 +46,10 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
acknowledged: false,
|
||||
showError: false
|
||||
showError: false,
|
||||
rules: {
|
||||
optionRequired: globalRules.OPTION_REQUIRED
|
||||
}
|
||||
};
|
||||
},
|
||||
emits: ["buttonClick"],
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
:availableLineItems="availableLineItems"
|
||||
@vapsItemsSelected="vapsItemsSelectedAction"
|
||||
v-on:link-event="openModalAction"
|
||||
validationRules="option-required"
|
||||
:validationRules="rules.optionRequired"
|
||||
isRequired
|
||||
/>
|
||||
<p class="caption disclaimer" v-html="PriceDisclaimerText"></p>
|
||||
|
|
@ -52,8 +52,7 @@ import { required } from '@/helpers/validation-rules';
|
|||
import { errorMessages } from '@/constants/error-messages';
|
||||
import { Form, defineRule } from 'vee-validate';
|
||||
import { allGlassPartsAndItemsHavePrices } from '@/layouts/service-packages/service-package-helper/service-package-helper'
|
||||
|
||||
defineRule('option-required', required(errorMessages.OPTION_REQUIRED));
|
||||
import globalRules from "@/constants/global-rules";
|
||||
|
||||
const store = useMainStore();
|
||||
|
||||
|
|
@ -112,7 +111,10 @@ export default {
|
|||
selectedVaps: [],
|
||||
availableLineItems: [],
|
||||
supportingItems: [],
|
||||
pricedGlassParts: []
|
||||
pricedGlassParts: [],
|
||||
rules: {
|
||||
optionRequired: globalRules.OPTION_REQUIRED
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@ import {vinLookupMethodSelections} from '@/constants/vin-lookup-methods';
|
|||
import { useMainStore } from '@/store';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import { mapStores } from "pinia";
|
||||
import { defineRule } from 'vee-validate';
|
||||
import { errorMessages } from '@/constants/error-messages';
|
||||
import globalRules from '@/constants/global-rules';
|
||||
import { required } from '@/helpers/validation-rules';
|
||||
|
||||
const pinia = createTestingPinia();
|
||||
useMainStore(pinia);
|
||||
|
|
@ -44,6 +48,7 @@ const siteFooterWidgetMockData = {
|
|||
};
|
||||
|
||||
function setupMocks() {
|
||||
defineRule(globalRules.OPTION_REQUIRED, required(errorMessages.OPTION_REQUIRED));
|
||||
const mockRoute = {
|
||||
query: {
|
||||
issPage: 'vehicle-lookup',
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
button-type-string="listButton"
|
||||
:question-text="questionTextFromCms"
|
||||
is-required
|
||||
validation-rules="option-required"
|
||||
:validation-rules="rules.optionRequired"
|
||||
data-test-id="vin-lookup-methods-button-question"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -13,32 +13,30 @@
|
|||
<script>
|
||||
|
||||
// Import Other Supporting Files
|
||||
import { defineRule } from 'vee-validate';
|
||||
import { required } from '@/helpers/validation-rules';
|
||||
import { errorMessages } from '@/constants/error-messages';
|
||||
import { useMainStore } from "@/store";
|
||||
import {vinLookupMethodSelections} from '@/constants/vin-lookup-methods';
|
||||
import { vinLookupMethodSelections } from '@/constants/vin-lookup-methods';
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import globalRules from '@/constants/global-rules';
|
||||
|
||||
// Import Component
|
||||
import ButtonQuestion from '@/digital-components/button-question/button-question.vue';
|
||||
|
||||
// Define Validation Rules
|
||||
defineRule('option-required', required(errorMessages.OPTION_REQUIRED));
|
||||
|
||||
export default {
|
||||
name: 'vin-lookup-methods',
|
||||
data() {
|
||||
return {
|
||||
questionTextFromCms: '',
|
||||
answersFromCms: [],
|
||||
rules: {
|
||||
optionRequired: globalRules.OPTION_REQUIRED
|
||||
}
|
||||
};
|
||||
},
|
||||
props: {
|
||||
modelValue: String,
|
||||
cmsWidgetName: {
|
||||
type: String,
|
||||
required,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
ref="policyNumber"
|
||||
disableAutoFill
|
||||
:isDisabled="isPolicyHolderEnabled"
|
||||
validationRules="policy-number-required" />
|
||||
:validationRules="rules.policyNumber" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-4" v-if="this.isCoverageEnabled">
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
v-model="welcomePageModel.policyZipCode"
|
||||
isRequired
|
||||
ref="policyZip"
|
||||
validationRules="policy-zip-required|policy-zip-format" />
|
||||
:validationRules="rules.policyZip" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-4">
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
disableAutoFill
|
||||
:max="new Date().toJSON().slice(0,10)"
|
||||
:min="'1972-12-01'"
|
||||
validationRules="loss-date-required"
|
||||
:validationRules="rules.lossDate"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
inputId="damageCauseQuestionField"
|
||||
:options="DamageCauseOptions"
|
||||
disableAutoFill
|
||||
validationRules="damage-option-required"
|
||||
:validationRules="rules.damageOption"
|
||||
placeHolderText="Select an option"
|
||||
id="welcomeDropdown"
|
||||
/>
|
||||
|
|
@ -71,7 +71,7 @@
|
|||
inputId="phoneNumberField"
|
||||
cmsWidgetName="PhoneNumberQuestion"
|
||||
v-model="welcomePageModel.phoneNumber"
|
||||
validationRules="phone-number-required|phone-number-format"
|
||||
:validationRules="rules.phoneNumber"
|
||||
isRequired
|
||||
ref="phoneNumber"
|
||||
mask="###-###-####"
|
||||
|
|
@ -85,7 +85,7 @@
|
|||
cmsWidgetName="EmailAddressQuestion"
|
||||
v-model="welcomePageModel.email"
|
||||
ref="email"
|
||||
validationRules="email-address-required|email-address-format"
|
||||
:validationRules="rules.email"
|
||||
isRequired
|
||||
disableAutoFill />
|
||||
</div>
|
||||
|
|
@ -101,7 +101,7 @@
|
|||
ref="damageCity"
|
||||
disableAutoFill
|
||||
v-if="this.displayDamageCityQuestion"
|
||||
validationRules="loss-city-required" />
|
||||
:validationRules="rules.lossCity" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
|
@ -113,7 +113,7 @@
|
|||
ref="state"
|
||||
inputId="8fdf9dc2e13e430eb57529499dceb3eb"
|
||||
:options="getStates"
|
||||
validationRules="loss-state-required"
|
||||
:validationRules="rules.lossState"
|
||||
isRequired
|
||||
disableAutoFill
|
||||
v-if="this.displayDamageStateQuestion"
|
||||
|
|
@ -127,11 +127,11 @@
|
|||
class="px-0 mt-4"
|
||||
cmsWidgetName="GlassOnlyQuestion"
|
||||
v-model="welcomePageModel.isDamageGlassOnly"
|
||||
inputId = "isDamageGlassOnly"
|
||||
inputId="isDamageGlassOnly"
|
||||
:answers="DamageGlassOnlyOptions"
|
||||
:questionText="DamageGlassOnlyQuestion"
|
||||
buttonTypeString="listButtonHorizontal"
|
||||
validationRules="damage-option-required"
|
||||
:validationRules="rules.damageOption"
|
||||
isRequired
|
||||
isSmallQuestionLabelText
|
||||
ref="glassOnlyDamage"
|
||||
|
|
@ -173,32 +173,17 @@
|
|||
import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
||||
import { useMainStore } from '@/store';
|
||||
import { states } from "@/constants/states"
|
||||
import globalRules from '@/constants/global-rules';
|
||||
|
||||
//define validation rules
|
||||
defineRule("loss-date-required", required(errorMessages.LOSS_DATE_REQUIRED));
|
||||
defineRule("loss-cause-required", required(errorMessages.LOSS_CAUSE_REQUIRED));
|
||||
defineRule("policy-number-required", required(errorMessages.POLICY_NUMBER_REQUIRED));
|
||||
defineRule("loss-date-required", required(errorMessages.LOSS_DATE_REQUIRED));
|
||||
defineRule("loss-state-required", required(errorMessages.LOSS_STATE_REQUIRED));
|
||||
defineRule("loss-city-required", required(errorMessages.LOSS_CITY_REQUIRED));
|
||||
defineRule("phone-number-required", required(errorMessages.PHONE_NUMBER_FORMAT));
|
||||
defineRule("email-address-required", required(errorMessages.EMAIL_ADDRESS_REQUIRED));
|
||||
defineRule("damage-option-required", required(errorMessages.DAMAGE_OPTION_REQUIRED));
|
||||
defineRule("policy-zip-required", required(errorMessages.POLICY_ZIP_REQUIRED));
|
||||
defineRule("policy-zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.POLICY_ZIP_FORMAT));
|
||||
|
||||
defineRule("email-address-format",
|
||||
regex(
|
||||
/^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9_\-.]+)\.([a-zA-Z]{2,})$/,
|
||||
errorMessages.EMAIL_ADDRESS_FORMAT
|
||||
));
|
||||
|
||||
defineRule("phone-number-format",
|
||||
regex(
|
||||
/^(\([0-9]{3}\)|[0-9]{3}) *[-.]? *[0-9]{3} *[-.]? *[0-9]{4}$/,
|
||||
errorMessages.PHONE_NUMBER_FORMAT
|
||||
));
|
||||
|
||||
export default {
|
||||
name: "welcome-page",
|
||||
mixins: [BaseFormMixin],
|
||||
|
|
@ -206,6 +191,16 @@ export default {
|
|||
return {
|
||||
welcomePageModel: this.getWelcomePageModelFromStore(),
|
||||
vehiclesFound: [],
|
||||
rules: {
|
||||
policyNumber: 'policy-number-required',
|
||||
policyZip: 'policy-zip-required|policy-zip-format',
|
||||
lossDate: 'loss-date-required',
|
||||
damageOption: 'damage-option-required',
|
||||
phoneNumber: `${globalRules.PHONE_NUMBER_REQUIRED}|${globalRules.PHONE_NUMBER_FORMAT}`,
|
||||
email: `${globalRules.EMAIL_ADDRESS_REQUIRED}|${globalRules.EMAIL_ADDRESS_FORMAT}`,
|
||||
lossCity: 'loss-city-required',
|
||||
lossState: 'loss-state-required'
|
||||
}
|
||||
};
|
||||
},
|
||||
setup() {
|
||||
|
|
|
|||
34
src/main.js
34
src/main.js
|
|
@ -1,32 +1,32 @@
|
|||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
import "../node_modules/bootstrap/dist/js/bootstrap.js";
|
||||
import 'bootstrap/dist/js/bootstrap';
|
||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
|
||||
import { useMainStore } from '@/store';
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
import baseMixin from '@/mixins/base-mixin';
|
||||
import { createPinia } from 'pinia';
|
||||
import Maska from "maska";
|
||||
import LoadScript from "vue-plugin-load-script";
|
||||
import Maska from 'maska';
|
||||
import LoadScript from 'vue-plugin-load-script';
|
||||
|
||||
import analyticsMixin from "@/mixins/analytics-mixin.js";
|
||||
import experimentMixin from "@/mixins/experiment-mixin.js";
|
||||
import analyticsMixin from '@/mixins/analytics-mixin';
|
||||
import experimentMixin from '@/mixins/experiment-mixin';
|
||||
import defineGlobalRules from '@/helpers/global-rule-definer';
|
||||
import router from './router';
|
||||
import App from './App.vue';
|
||||
|
||||
// Vue App Setup
|
||||
const vueApp = createApp(App);
|
||||
|
||||
/**
|
||||
/**
|
||||
* Needed to make injections reactively linked to the provider.
|
||||
* This is not needed once Vue.js is in version 3.3
|
||||
* https://vuejs.org/guide/components/provide-inject.html#working-with-reactivity
|
||||
*/
|
||||
vueApp.config.unwrapInjectedRef = true;
|
||||
|
||||
vueApp.config.compilerOptions.isCustomElement = (tag) => {
|
||||
return (tag === 'siteSubHeader' ||
|
||||
tag === 'ServicePackages' ||
|
||||
tag === 'servicePackageQuestion');
|
||||
}
|
||||
vueApp.config.compilerOptions.isCustomElement = (tag) =>
|
||||
(tag === 'siteSubHeader'
|
||||
|| tag === 'ServicePackages'
|
||||
|| tag === 'servicePackageQuestion');
|
||||
|
||||
// Pinia
|
||||
const pinia = createPinia();
|
||||
|
|
@ -34,7 +34,6 @@ vueApp.use(pinia);
|
|||
pinia.use(piniaPluginPersistedstate);
|
||||
useMainStore().populateInitialState();
|
||||
|
||||
|
||||
// Additional Vue items to setup
|
||||
vueApp.use(router);
|
||||
vueApp.use(Maska);
|
||||
|
|
@ -43,4 +42,7 @@ vueApp.mixin(baseMixin);
|
|||
vueApp.mixin(analyticsMixin);
|
||||
vueApp.mixin(experimentMixin);
|
||||
|
||||
vueApp.mount("#app");
|
||||
vueApp.mount('#app');
|
||||
|
||||
// define global rules
|
||||
defineGlobalRules();
|
||||
|
|
|
|||
2651
src/store/index.js
2651
src/store/index.js
File diff suppressed because it is too large
Load diff
|
|
@ -2,7 +2,7 @@ import { useMainStore } from '@/store';
|
|||
import { createApp } from 'vue';
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import globalMethods from "@/global-methods";
|
||||
import App from '@/App.vue';
|
||||
import App from '@/App';
|
||||
import { getRandomString, getRandomGuid, getRandomInt, getRandomBoolean } from '@/helpers/data-generation';
|
||||
import { coverageStatuses } from "@/constants/coverage-statuses.js";
|
||||
|
||||
|
|
@ -422,5 +422,45 @@ describe("Store", () => {
|
|||
expect(store.payment.insuranceCoverage.isVerified).toBe(false);
|
||||
expect(store.payment.insuranceCoverage.coverageStatus).toBe(coverageStatuses.PENDING);
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateContactInfo method', () => {
|
||||
it('UpdateContactInfo updates contact info in store', () => {
|
||||
// Arrange
|
||||
const firstName = getRandomString(4, 10);
|
||||
const lastName = getRandomString(5, 15);
|
||||
const emailAddress = false;
|
||||
const phoneNumber = getRandomInt(1000000000, 9999999999);
|
||||
const requestTextUpdates = getRandomBoolean();
|
||||
const notesForTechnician = getRandomString(50, 150);
|
||||
|
||||
// Act
|
||||
store.updateContactInfo({ firstName,
|
||||
lastName,
|
||||
emailAddress,
|
||||
phoneNumber,
|
||||
requestTextUpdates,
|
||||
notesForTechnician });
|
||||
|
||||
// Assert
|
||||
expect(store.contactInfo.firstName).toEqual(firstName);
|
||||
expect(store.contactInfo.lastName).toEqual(lastName);
|
||||
expect(store.contactInfo.emailAddress).toEqual(emailAddress);
|
||||
expect(store.contactInfo.phoneNumber).toEqual(phoneNumber);
|
||||
expect(store.contactInfo.requestTextUpdates).toEqual(requestTextUpdates);
|
||||
expect(store.contactInfo.notesForTechnician).toEqual(notesForTechnician);
|
||||
});
|
||||
it('All null values => contact info set in store to all nulls', () => {
|
||||
// Act
|
||||
store.updateContactInfo({});
|
||||
|
||||
// Assert
|
||||
expect(store.contactInfo.firstName).toEqual('');
|
||||
expect(store.contactInfo.lastName).toEqual('');
|
||||
expect(store.contactInfo.emailAddress).toEqual('');
|
||||
expect(store.contactInfo.phoneNumber).toEqual('');
|
||||
expect(store.contactInfo.requestTextUpdates).toEqual(false);
|
||||
expect(store.contactInfo.notesForTechnician).toEqual('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@ html {
|
|||
}
|
||||
}
|
||||
|
||||
&.list-card {
|
||||
border: 1px solid $red;
|
||||
}
|
||||
|
||||
&.ui-radio {
|
||||
&:hover {
|
||||
input[type="radio"],
|
||||
|
|
|
|||
|
|
@ -75,4 +75,12 @@ describe("checkbox.vue", () => {
|
|||
|
||||
expect(paragraph.text()).toEqual("screenreader text");
|
||||
});
|
||||
|
||||
it('Default isChecked is false', async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(checkbox, {});
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.isChecked).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@
|
|||
:id="buttonID"
|
||||
:tabindex="tabIndex"
|
||||
:aria-required="isRequired"
|
||||
:checked="isChecked"
|
||||
@change="handleCheckChange"
|
||||
:ref="checkboxName"
|
||||
/>
|
||||
:ref="checkboxName" />
|
||||
<label class="d-flex align-items-start" :for="buttonID">
|
||||
<p v-if="checkboxLabel" class="m-0" v-html="checkboxLabel"></p>
|
||||
<span v-if="screenReaderOnlyText" class="sr-only">{{ screenReaderOnlyText }}</span>
|
||||
|
|
@ -30,6 +30,10 @@ export default {
|
|||
screenReaderOnlyText: String,
|
||||
isRequired: Boolean,
|
||||
hasError: Boolean,
|
||||
isChecked: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleCheckChange() {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,15 @@
|
|||
<template>
|
||||
<a v-if="linkType === 'navigation'" @click="handleClick" class="navigation-link" :href="href">
|
||||
{{text}}
|
||||
<slot name="after-text"></slot>
|
||||
{{text}}<slot name="after-text"></slot>
|
||||
</a>
|
||||
<a v-else-if="linkType === 'footer'" @click="handleClick" class="footer-link" :href="href" target="_blank">
|
||||
{{text}}
|
||||
<slot name="after-text"></slot>
|
||||
{{text}}<slot name="after-text"></slot>
|
||||
</a>
|
||||
<a v-else-if="linkType === 'textSmall'" @click="handleClick" class="small" :href="href">
|
||||
{{text}}
|
||||
<slot name="after-text"></slot>
|
||||
{{text}}<slot name="after-text"></slot>
|
||||
</a>
|
||||
<a v-else-if="linkType === 'text'" @click="handleClick" :href="href">
|
||||
{{text}}
|
||||
<slot name="after-text"></slot>
|
||||
{{text}}<slot name="after-text"></slot>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue