diff --git a/package-lock.json b/package-lock.json index 4569f488..de029cb4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "eslint-plugin-vue": "^9.15.1", "jest": "^27.0.5", "jest-junit": "^13.0.0", + "jest-serializer-vue": "^3.1.0", "jsdoc": "^4.0.2", "jsdom": "^22.1.0", "sass": "^1.32.7", @@ -4413,6 +4414,15 @@ } } }, + "node_modules/@vue/cli-plugin-unit-jest/node_modules/jest-serializer-vue": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jest-serializer-vue/-/jest-serializer-vue-2.0.2.tgz", + "integrity": "sha512-nK/YIFo6qe3i9Ge+hr3h4PpRehuPPGZFt8LDBdTHYldMb7ZWlkanZS8Ls7D8h6qmQP2lBQVDLP0DKn5bJ9QApQ==", + "dev": true, + "dependencies": { + "pretty": "2.0.0" + } + }, "node_modules/@vue/cli-plugin-vuex": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-5.0.8.tgz", @@ -12245,9 +12255,9 @@ } }, "node_modules/jest-serializer-vue": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jest-serializer-vue/-/jest-serializer-vue-2.0.2.tgz", - "integrity": "sha512-nK/YIFo6qe3i9Ge+hr3h4PpRehuPPGZFt8LDBdTHYldMb7ZWlkanZS8Ls7D8h6qmQP2lBQVDLP0DKn5bJ9QApQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jest-serializer-vue/-/jest-serializer-vue-3.1.0.tgz", + "integrity": "sha512-vXz9/3IgBbLhsaVANYLG4ROCQd+Wg3qbB6ICofzFL+fbhSFPlqb0/MMGXcueVsjaovdWlYiRaLQLpdi1PTcoRQ==", "dev": true, "dependencies": { "pretty": "2.0.0" diff --git a/package.json b/package.json index dbbcf42e..36f4c832 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", - "test:unit": "vue-cli-service test:unit --coverage --ci", + "test:unit": "vue-cli-service test:unit --coverage --ci --colors", "test:unit:lite": "vue-cli-service test:unit --ci" }, "dependencies": { @@ -50,6 +50,7 @@ "eslint-plugin-vue": "^9.15.1", "jest": "^27.0.5", "jest-junit": "^13.0.0", + "jest-serializer-vue": "^3.1.0", "jsdoc": "^4.0.2", "jsdom": "^22.1.0", "sass": "^1.32.7", diff --git a/src/constants/cms-widget-fields.js b/src/constants/cms-widget-fields.js new file mode 100644 index 00000000..f8fd23fe --- /dev/null +++ b/src/constants/cms-widget-fields.js @@ -0,0 +1,45 @@ +const widgetFields = Object.freeze({ + HEADER_WIDGET: { + NAME: 'Name', + ANSWERS: 'Answers' + }, + SUB_HEADER_WIDGET: { + NAME: 'Name', + SUB_HEADER_TEXT: 'SubHeaderText', + SECONDARY_TEXT: 'SecondaryText', + BACK_BUTTON_ACCESSIBLE_TEXT: 'BackButtonAccessibleText' + }, + INPUT_QUESTION_WIDGET: { + NAME: 'Name', + QUESTION_TEXT: 'QuestionText', + BUTTON_TEXT: 'ButtonText', + ANSWERS: 'Answers' + }, + TEXT_BLOCK_WIDGET: { + NAME: 'Name', + TEXT: 'Text' + }, + CONTENT_GROUP_WIDGET: { + NAME: 'Name', + HEADER_TEXT: 'HeaderText', + SUBHEADER_TEXT: 'SubheaderText', + BODY_TEXT: 'BodyText', + BODY_TEXT_2: 'BodyText2', + FOOTER_TEXT: 'FooterText', + IMAGE: 'Image' + }, + ALERT_WIDGET: { + NAME: 'Name', + HEADLINE_TEXT: 'HeadlineText', + BODY_TEXT: 'BodyText' + }, + FOOTER_WIDGET: { + NAME: 'Name', + BACK_BUTTON_TEXT: 'BackButtonText', + FORWARD_BUTTON_TEXT: 'ForwardButtonText', + FOOTER_IMAGE: 'FooterImage', + ALT_TEXT: 'AltText' + } +}); + +export default widgetFields; diff --git a/src/constants/global-rules.js b/src/constants/global-rules.js index d8c6f406..da36b822 100644 --- a/src/constants/global-rules.js +++ b/src/constants/global-rules.js @@ -13,6 +13,8 @@ const globalRules = Object.freeze({ EMAIL_ADDRESS_FORMAT: 'email-address-format', PHONE_NUMBER_REQUIRED: 'phone-number-required', PHONE_NUMBER_FORMAT: 'phone-number-format', + ZIP_CODE_REQUIRED: 'zip-code-required', + ZIP_CODE_SEARCH_FORMAT: 'zip-code-search-format', OPTION_REQUIRED: 'option-required' }); diff --git a/src/digital-components/textbox-question/textbox-question.spec.js b/src/digital-components/textbox-question/textbox-question.spec.js index c98e7837..f7a31e03 100644 --- a/src/digital-components/textbox-question/textbox-question.spec.js +++ b/src/digital-components/textbox-question/textbox-question.spec.js @@ -1,4 +1,5 @@ import { shallowMount } from '@vue/test-utils'; +import { useField, validate } from 'vee-validate'; import textboxQuestion from '@/digital-components/textbox-question/textbox-question.vue'; // Mock CMS content @@ -8,150 +9,946 @@ const mockMixin = { getCmsContent: jest.fn().mockImplementation(() => questionText) } }; -const maska = jest.fn(); -describe('textboxQuestion.vue', () => { - it('Should render a text input', async () => { - // Arrange - const wrapper = shallowMount(textboxQuestion, { - global: { - directives: { - maska - } - }, - mixins: [mockMixin] +jest.mock('vee-validate', () => ({ + useField: jest.fn(), + validate: jest.fn() +})); + +useField.mockImplementation(() => Promise.resolve({})); +validate.mockImplementation(() => Promise.resolve({})); + +const searchButtonSelector = 'button[type="submit"][aria-label="Search button"]'; +const selectButtonSelector = 'button[type="submit"][aria-label="Select button"]'; +const errorMessageSelector = { ref: 'errorMessageDiv' }; + +describe('TextboxQuestion', () => { + describe('should render', () => { + describe('an input element', () => { + test('that exists with expected classes', () => { + // Arrange + const wrapper = shallowMount(textboxQuestion, { + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.exists()).toBeTruthy(); + expect(input.classes()).toContain('form-control'); + }); + describe('with correct attributes and classes given prop', () => { + test('type', () => { + // Arrange + const type = 'crocodile'; + const wrapper = shallowMount(textboxQuestion, { + propsData: { type }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.attributes().type).toBe(type); + }); + test('placeholderText', () => { + // Arrange + const placeholderText = 'text in place'; + const wrapper = shallowMount(textboxQuestion, { + propsData: { placeholderText }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.attributes().placeholder).toBe(placeholderText); + }); + test('inputId', () => { + // Arrange + const inputId = '123009'; + const wrapper = shallowMount(textboxQuestion, { + propsData: { inputId }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.attributes('id')).toBe(inputId); + expect(input.attributes('name')).toBe(inputId); + }); + describe('isDisabled', () => { + test('with value "true"', () => { + // Arrange + const isDisabled = true; + const wrapper = shallowMount(textboxQuestion, { + propsData: { isDisabled }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.attributes('aria-disabled')).toBe(String(isDisabled)); + expect(input.attributes().disabled).toBeDefined(); + }); + test('with value "false"', () => { + // Arrange + const isDisabled = false; + const wrapper = shallowMount(textboxQuestion, { + propsData: { isDisabled }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.attributes('aria-disabled')).toBe(String(isDisabled)); + expect(input.attributes().disabled).not.toBeDefined(); + }); + }); + describe('isRequired', () => { + test('with value "true"', () => { + // Arrange + const isRequired = true; + const wrapper = shallowMount(textboxQuestion, { + propsData: { isRequired }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.attributes().required).toBeDefined(); + }); + test('with value "false"', () => { + // Arrange + const isRequired = false; + const wrapper = shallowMount(textboxQuestion, { + propsData: { isRequired }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.attributes().required).not.toBeDefined(); + }); + }); + describe('hasIcon', () => { + test('with value "true"', () => { + // Arrange + const hasIcon = true; + const wrapper = shallowMount(textboxQuestion, { + propsData: { hasIcon }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.classes()).toContain('has-icon'); + }); + test('with value "false"', () => { + // Arrange + const hasIcon = false; + const wrapper = shallowMount(textboxQuestion, { + propsData: { hasIcon }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.classes()).not.toContain('has-icon'); + }); + }); + describe('iconRight', () => { + test('with value "true"', () => { + // Arrange + const iconRight = true; + const wrapper = shallowMount(textboxQuestion, { + propsData: { iconRight }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.classes()).toContain('icon-right'); + }); + test('with value "false"', () => { + // Arrange + const iconRight = false; + const wrapper = shallowMount(textboxQuestion, { + propsData: { iconRight }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.classes()).not.toContain('icon-right'); + }); + }); + test('validationRules', () => { + // Arrange + const validationRules = 'random validation rules'; + const wrapper = shallowMount(textboxQuestion, { + propsData: { validationRules }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.attributes().validationrules).toBe(validationRules); + }); + test('cmsWidgetName', () => { + // Arrange + const cmsWidgetName = 'name of widget'; + const wrapper = shallowMount(textboxQuestion, { + propsData: { cmsWidgetName }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + const expected = '#name of widget'; + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.attributes('data-bs-target')).toBe(expected); + }); + describe('maxLength', () => { + test('with no value', () => { + // Arrange + const wrapper = shallowMount(textboxQuestion, { + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + const expected = '999'; + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.attributes('maxlength')).toBe(expected); + }); + test('with some value', () => { + // Arrange + const maxLength = '123'; + const wrapper = shallowMount(textboxQuestion, { + propsData: { maxLength }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.attributes('maxlength')).toBe(maxLength); + }); + }); + describe('cornerStyle', () => { + test('with value "rounded"', () => { + // Arrange + const cornerStyle = 'rounded'; + const wrapper = shallowMount(textboxQuestion, { + propsData: { cornerStyle }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.classes()).toContain('rounded-pill'); + }); + test('with value other than "rounded"', () => { + // Arrange + const cornerStyle = 'decidedly not rounded'; + const wrapper = shallowMount(textboxQuestion, { + propsData: { cornerStyle }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.classes()).not.toContain('rounded-pill'); + }); + }); + describe('includeSelectIcon', () => { + test('with value "true"', () => { + // Arrange + const includeSelectIcon = true; + const wrapper = shallowMount(textboxQuestion, { + propsData: { includeSelectIcon }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.attributes('data-bs-toggle')).toBe('modal'); + }); + test('with value "false"', () => { + // Arrange + const includeSelectIcon = false; + const wrapper = shallowMount(textboxQuestion, { + propsData: { includeSelectIcon }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.attributes('data-bs-toggle')).toBe(''); + }); + }); + test('min', () => { + // Arrange + const min = 'some value'; + const wrapper = shallowMount(textboxQuestion, { + propsData: { min }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.attributes().min).toBe(min); + }); + test('max', () => { + // Arrange + const max = 'some value'; + const wrapper = shallowMount(textboxQuestion, { + propsData: { max }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const input = wrapper.find('input'); + + // Assert + expect(input.attributes().max).toBe(max); + }); + }); }); + describe('a label element given that displayQuestionText is true with correct attributes and classes set with prop', () => { + const displayQuestionText = true; - wrapper.getCmsContent = jest.fn(); + test('inputId', () => { + // Arrange + const inputId = '123758'; + const wrapper = shallowMount(textboxQuestion, { + propsData: { displayQuestionText, inputId }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); - // Act - const input = wrapper.find('input'); + // Act + const label = wrapper.find('label'); - // Assert - expect(input.exists()).toBe(true); + // Assert + expect(label.exists()).toBeTruthy(); + expect(label.attributes().for).toBe(inputId); + expect(label.attributes('aria-label')).toBe(questionText); + expect(label.classes()).toContain('form-label'); + }); + describe('questionAlignment', () => { + test('having value "center"', () => { + // Arrange + const questionAlignment = 'center'; + const wrapper = shallowMount(textboxQuestion, { + propsData: { displayQuestionText, questionAlignment }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const label = wrapper.find('label'); + + // Assert + expect(label.exists()).toBeTruthy(); + expect(label.attributes('aria-label')).toBe(questionText); + expect(label.classes()).toContain('form-label'); + expect(label.classes()).toContain('text-center'); + expect(label.classes()).toContain('w-100'); + expect(label.classes()).toContain('mb-5'); + }); + test('having value other than "center"', () => { + // Arrange + const questionAlignment = 'not center'; + const wrapper = shallowMount(textboxQuestion, { + propsData: { displayQuestionText, questionAlignment }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const label = wrapper.find('label'); + + // Assert + expect(label.exists()).toBeTruthy(); + expect(label.attributes('aria-label')).toBe(questionText); + expect(label.classes()).toContain('form-label'); + expect(label.classes()).not.toContain('text-center'); + expect(label.classes()).not.toContain('w-100'); + expect(label.classes()).not.toContain('mb-5'); + }); + }); + }); + test('a search button with the correct attributes and classes if includeSearchIcon is true', () => { + // Arrange + const includeSearchIcon = true; + const wrapper = shallowMount(textboxQuestion, { + propsData: { includeSearchIcon }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const searchButton = wrapper.find(searchButtonSelector); + + // Assert + expect(searchButton.exists()).toBeTruthy(); + expect(searchButton.attributes('type')).toBe('submit'); + expect(searchButton.attributes('aria-label')).toBe('Search button'); + }); + describe('a select button with the correct attributes and classes if includeSelectIcon is true', () => { + const includeSelectIcon = true; + test('where cmsWidgetName defined', () => { + // Arrange + const cmsWidgetName = 'name of cms widget'; + const wrapper = shallowMount(textboxQuestion, { + propsData: { includeSelectIcon, cmsWidgetName }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + const expectedDataBsTarget = '#name of cms widget'; + + // Act + const selectButton = wrapper.find(selectButtonSelector); + + // Assert + expect(selectButton.exists()).toBeTruthy(); + expect(selectButton.attributes().type).toBe('submit'); + expect(selectButton.attributes('data-bs-toggle')).toBe('modal'); + expect(selectButton.attributes('data-bs-target')).toBe(expectedDataBsTarget); + expect(selectButton.attributes('aria-label')).toBe('Select button'); + }); + test('where cmsWidgetName not defined', () => { + // Arrange + const wrapper = shallowMount(textboxQuestion, { + propsData: { includeSelectIcon }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + const expectedDataBsTarget = '#'; + + // Act + const selectButton = wrapper.find(selectButtonSelector); + + // Assert + expect(selectButton.exists()).toBeTruthy(); + expect(selectButton.attributes().type).toBe('submit'); + expect(selectButton.attributes('data-bs-toggle')).toBe('modal'); + expect(selectButton.attributes('data-bs-target')).toBe(expectedDataBsTarget); + expect(selectButton.attributes('aria-label')).toBe('Select button'); + }); + }); + test('an error with the correct attributes and classes if errorMessage is not empty', () => { + // Arrange + const errorMessage = 'some not empty message'; + useField.mockReturnValueOnce({ errorMessage }); + const wrapper = shallowMount(textboxQuestion, { + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const error = wrapper.find(errorMessageSelector); + + // Assert + expect(error.exists()).toBeTruthy(); + expect(error.attributes().style).not.toBeDefined(); + expect(error.classes()).toContain('form-test-error'); + expect(error.classes()).toContain('row'); + expect(error.classes()).toContain('my-1'); + }); + }); + describe('does not render', () => { + test('a label element if displayQuestionText is false', () => { + // Arrange + const displayQuestionText = false; + const wrapper = shallowMount(textboxQuestion, { + propsData: { displayQuestionText }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const label = wrapper.find('label'); + + // Assert + expect(label.exists()).not.toBeTruthy(); + }); + test('a search button element if includeSearchIcon is false', () => { + // Arrange + const includeSearchIcon = false; + const wrapper = shallowMount(textboxQuestion, { + propsData: { includeSearchIcon }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const searchButton = wrapper.find(searchButtonSelector); + + // Assert + expect(searchButton.exists()).not.toBeTruthy(); + }); + test('a select button if includeSelectIcon is false', () => { + // Arrange + const includeSelectIcon = false; + const wrapper = shallowMount(textboxQuestion, { + propsData: { includeSelectIcon }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const selectButton = wrapper.find(selectButtonSelector); + + // Assert + expect(selectButton.exists()).not.toBeTruthy(); + }); + test('an error span if errorMessage is empty', () => { + // Arrange + const errorMessage = ''; + const wrapper = shallowMount(textboxQuestion, { + propsData: { errorMessage }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + const errorSpan = wrapper.find(errorMessageSelector); + + // Assert + expect(errorSpan.attributes().style).toContain('display: none;'); + }); }); - it("Should render the 'questionText' data value as the label text when disableAutoFill is false.", async () => { - // Arrange - const wrapper = shallowMount(textboxQuestion, { - global: { - directives: { - maska - } - }, - mixins: [mockMixin] - }); - - // Act - const label = wrapper.find('label'); - - // Assert - expect(label.text()).toContain(questionText); - }); - - it('Should return input id as the id of the input field', async () => { - // Arrange - const wrapper = shallowMount(textboxQuestion, { - global: { - directives: { - maska - } - }, - propsData: { - inputId: 'input ID' - }, - mixins: [mockMixin] - }); - - // Act - const input = wrapper.find('input'); - - // Assert - expect(input.attributes().id).toEqual('input ID'); - }); - - it("Should render the 'questionText' data value as the aria-label attribute.", async () => { + test('should return the errorMessage, handleBlur, handleChange, meta, validate and errors values from the useField hook', () => { // Arrange + const errorMessage = 'some not empty message'; + const handleBlur = () => { console.log('blur'); }; + const handleChange = () => { console.log('change'); }; + const meta = 'meta content'; + const setValidate = () => { console.log('validate'); }; + const errors = 'content of errors'; + useField.mockReturnValueOnce({ + errorMessage, handleBlur, handleChange, meta, errors, validate: setValidate + }); const wrapper = shallowMount(textboxQuestion, { + mixins: [mockMixin], global: { - directives: { - maska - } - }, - mixins: [mockMixin] + directives: { maska: jest.fn() } + } }); - // Act - const label = wrapper.find('label'); - // Assert - expect(label.attributes('aria-label')).toContain(questionText); + expect(wrapper.vm.errorMessage).toBe(errorMessage); + expect(wrapper.vm.handleBlur).toBe(handleBlur); + expect(wrapper.vm.handleChange).toBe(handleChange); + expect(wrapper.vm.meta).toBe(meta); + expect(wrapper.vm.validate).toBe(setValidate); + expect(wrapper.vm.errors).toBe(errors); }); - it('Should return aria-disabled state as disabled', async () => { - // Arrange - const wrapper = shallowMount(textboxQuestion, { - global: { - directives: { - maska + describe('computed', () => { + test('"questionText" should return value based on getCmsContent result', () => { + // Arrange + const cmsContent = 'content from cms'; + mockMixin.methods.getCmsContent.mockReturnValueOnce(cmsContent); + const wrapper = shallowMount(textboxQuestion, { + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } } - }, - propsData: { - isDisabled: true - }, - mixins: [mockMixin] + }); + + // Assert + expect(wrapper.vm.questionText).toBe(cmsContent); }); + describe('"value" should', () => { + test('get the modelValue prop', () => { + // Arrange + const modelValue = 'value of modelValue'; + const wrapper = shallowMount(textboxQuestion, { + propsData: { modelValue }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); - // Assert - const input = wrapper.find('input'); + // Assert + expect(wrapper.vm.value).toBe(modelValue); + }); + test('emit the "update:modelValue" event when updated', () => { + // Arrange + const wrapper = shallowMount(textboxQuestion, { + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + const newValue = 'updated value'; - // Expect - expect(input.attributes('aria-disabled')).toEqual('true'); + // Act + wrapper.vm.value = newValue; + + // Assert + expect(wrapper.emitted('update:modelValue')).toBeTruthy(); + expect(wrapper.emitted('update:modelValue').length).toBe(1); // event emitted once + expect(wrapper.emitted('update:modelValue')[0]).toEqual([newValue]); // emitted with expected payload + }); + }); + describe('labelText', () => { + test('should return the question text with no break characters if disableAutoFill is true', () => { + // Arrange + const disableAutoFill = true; + const wrapper = shallowMount(textboxQuestion, { + propsData: { disableAutoFill }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + // TODO this seems like strange behavior to me + const expectedLabelText = 'Q⁠uestion T⁠ext'; + + // Assert + expect(wrapper.vm.labelText).toBe(expectedLabelText); + }); + test('should return the question text as it is if disableAutoFill is false', () => { + // Arrange + const disableAutoFill = false; + const wrapper = shallowMount(textboxQuestion, { + propsData: { disableAutoFill }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Assert + expect(wrapper.vm.labelText).toBe(questionText); + }); + }); }); - it('Should emit new value when modelValue is changed', async () => { - // Act - const wrapper = shallowMount(textboxQuestion, { - global: { - directives: { - maska + describe('watch', () => { + test('should validate the new value and trigger full validation if valid', async () => { + // Arrange + const handleChange = jest.fn(); + const modelValue = 'initial value'; + const validationRules = 'required'; + validate.mockReturnValueOnce({ valid: true }); + useField.mockReturnValueOnce({ handleChange }); + const wrapper = shallowMount(textboxQuestion, { + propsData: { modelValue, validationRules }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } } - }, - propsData: { - modelValue: 'val' - }, - mixins: [mockMixin] + }); + const newValue = 'new value'; + + // Act + await wrapper.vm.$options.watch.value.call(wrapper.vm, newValue); + + // Assert + expect(validate).toHaveBeenCalledWith(newValue, validationRules); + expect(handleChange).toHaveBeenCalledWith(newValue); }); + test('should not trigger full validation if invalid', async () => { + // Arrange + const handleChange = jest.fn(); + const modelValue = 'initial value'; + const validationRules = 'required'; + validate.mockReturnValueOnce({ valid: false }); + useField.mockReturnValueOnce({ handleChange }); + const wrapper = shallowMount(textboxQuestion, { + propsData: { modelValue, validationRules }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + const newValue = 'failing value'; - await wrapper.find('input').setValue('val2'); + // Act + await wrapper.vm.$options.watch.value.call(wrapper.vm, newValue); - // Assert - expect(wrapper.emitted()).toHaveProperty('change'); + // Assert + expect(validate).toHaveBeenCalledWith(newValue, validationRules); + expect(handleChange).not.toHaveBeenCalled(); + }); + test('should emit new value when modelValue is changed', async () => { + // Act + const wrapper = shallowMount(textboxQuestion, { + global: { + directives: { maska: jest.fn() } + }, + propsData: { + modelValue: 'val' + }, + mixins: [mockMixin] + }); + await wrapper.find('input').setValue('val2'); + + // Assert + expect(wrapper.emitted()).toHaveProperty('change'); + }); }); - // TODO Correct test so it actually calls toHaveBeenCalled -> () <- - it.skip('Should call this.handleChange with new value when the value is changed and the new value is valid', async () => { - // Arrange - const wrapper = shallowMount(textboxQuestion, { - global: { - directives: { - maska + describe('mounted hook', () => { + test('should emit an inputId assigned event with the inputId prop as an argument', () => { + // Arrange + const inputId = '126743'; + const wrapper = shallowMount(textboxQuestion, { + propsData: { inputId }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } } - }, - propsData: { - options: {}, - modelValue: 'foo' - }, - mixins: [mockMixin] + }); + + // Assert + expect(wrapper.emitted()['textboxQuestionEvent.inputIdAssigned']).toBeTruthy(); + expect(wrapper.emitted()['textboxQuestionEvent.inputIdAssigned'][0]).toEqual([inputId]); }); + }); - wrapper.vm.handleChange = jest.fn().mockImplementation(() => {}); - wrapper.vm.validate = jest.fn().mockImplementation(() => true); + describe('method', () => { + describe('trimOnPaste', () => { + test('should trim the pasted text and update the model value', () => { + // Arrange + document.getElementById = jest.fn(() => ({ + value: ' hello ', + selectionStart: 7, + selectionEnd: 7 + })); + const stopPropagation = jest.fn(); + const preventDefault = jest.fn(); + const data = { getData: jest.fn(() => ' world ') }; + const inputId = '123758'; + const pasteEvent = { + type: 'paste', + srcElement: { id: inputId }, + clipboardData: data, + stopPropagation, + preventDefault + }; + const wrapper = shallowMount(textboxQuestion, { + propsData: { inputId }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); - // Act - wrapper.vm.$options.watch.value.call(wrapper.vm, 'bar'); + // Act + wrapper.vm.trimOnPaste(pasteEvent); - // Assert - expect(wrapper.vm.handleChange).toHaveBeenCalled; + // Assert + expect(stopPropagation).toHaveBeenCalled(); + expect(preventDefault).toHaveBeenCalled(); + expect(data.getData).toHaveBeenCalledWith('Text'); + expect(wrapper.emitted()['update:modelValue']).toBeTruthy(); + expect(wrapper.emitted()['update:modelValue'][0]).toEqual([' hello world ']); + }); + test('should trim the dropped text and update the model value', () => { + // Arrange + document.getElementById = jest.fn(() => ({ + value: ' hello ', + selectionStart: 7, + selectionEnd: 7 + })); + const stopPropagation = jest.fn(); + const preventDefault = jest.fn(); + const data = { getData: jest.fn(() => ' world ') }; + const inputId = '123758'; + const dropEvent = { + type: 'drop', + srcElement: { id: inputId }, + dataTransfer: data, + stopPropagation, + preventDefault + }; + const wrapper = shallowMount(textboxQuestion, { + propsData: { inputId }, + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + wrapper.vm.trimOnPaste(dropEvent); + + // Assert + expect(stopPropagation).toHaveBeenCalled(); + expect(preventDefault).toHaveBeenCalled(); + expect(data.getData).toHaveBeenCalledWith('Text'); + expect(wrapper.emitted()['update:modelValue']).toBeTruthy(); + expect(wrapper.emitted()['update:modelValue'][0]).toEqual([' hello world ']); + }); + }); + describe('clickedSearch', () => { + test('should emit a click event if the meta valid flag is true', () => { + // Arrange + const meta = { valid: true }; + useField.mockReturnValueOnce({ meta }); + const wrapper = shallowMount(textboxQuestion, { + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + wrapper.vm.clickedSearch(); + + // Assert + expect(wrapper.emitted()['click-event']).toBeTruthy(); + }); + test('should not emit a click event if the meta valid flag is falsy', () => { + // Arrange + const meta = { }; + useField.mockReturnValueOnce({ meta }); + const wrapper = shallowMount(textboxQuestion, { + mixins: [mockMixin], + global: { + directives: { maska: jest.fn() } + } + }); + + // Act + wrapper.vm.clickedSearch(); + + // Assert + expect(wrapper.emitted()['click-event']).not.toBeTruthy(); + }); + }); }); }); diff --git a/src/digital-components/textbox-question/textbox-question.vue b/src/digital-components/textbox-question/textbox-question.vue index b619460e..86bcfeea 100644 --- a/src/digital-components/textbox-question/textbox-question.vue +++ b/src/digital-components/textbox-question/textbox-question.vue @@ -30,7 +30,7 @@ :aria-label="questionText" :min="min" :max="max" - required + :required="isRequired" :class="[ hasIcon ? 'has-icon' : '', iconRight ? 'icon-right' : '', @@ -48,7 +48,8 @@ + aria-label="Search button" + @click="clickedSearch" /> diff --git a/src/router/index.js b/src/router/index.js index ca1609f9..06c209b1 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -154,7 +154,8 @@ router.navigateWithoutSaving = ( optionalParams = {}, optionalPageData = {} ) => { - navigate(scenario, currentRoute, false, optionalQuery, optionalParams, optionalPageData); + // TODO does not save session + navigate(scenario, currentRoute, optionalQuery, optionalParams, optionalPageData); }; // Get route information by page name. @@ -252,7 +253,7 @@ function navigate( query: Object.assign(optionalQuery, { issPage: matchingScenarioMap.destinationIssPageValue }), - params: optionalParams + params: optionalParams // TODO I don't think this actually works }); } else if (matchingScenarioMap.destinationUrl) { navigateToUrl(matchingScenarioMap.destinationUrl, optionalQuery); diff --git a/src/router/router-constants/navigation-scenarios.js b/src/router/router-constants/navigation-scenarios.js index 1c6c1a13..69c61090 100644 --- a/src/router/router-constants/navigation-scenarios.js +++ b/src/router/router-constants/navigation-scenarios.js @@ -63,6 +63,9 @@ const navigationScenarios = Object.freeze({ CLICKED_BACK_WITH_REPAIR: 'CLICKED_BACK_WITH_REPAIR', CLICKED_FORWARD_WITH_INVALID_STATE: 'CLICKED_FORWARD_WITH_INVALID_STATE', + // TPA Search + CLICKED_DO_NOT_SEE_MY_SHOP_LINK: 'CLICKED_DO_NOT_SEE_MY_SHOP_LINK', + // Provider Preference CLICKED_FORWARD_WITH_SAFELITE: 'CLICKED_FORWARD_WITH_SAFELITE', CLICKED_FORWARD_WITH_TPA_ENABLED: 'CLICKED_FORWARD_WITH_TPA_ENABLED', diff --git a/src/router/router-constants/router-params.js b/src/router/router-constants/router-params.js index 2465f965..e5758bf4 100644 --- a/src/router/router-constants/router-params.js +++ b/src/router/router-constants/router-params.js @@ -1,6 +1,7 @@ const routerParams = Object.freeze({ DISPLAY_VEHICLE_CHANGE_ALERT: 'displayVehicleChangeAlert', - SAVE_SESSION_SYNCHRONOUS: 'saveSessionSynchronous' + SAVE_SESSION_SYNCHRONOUS: 'saveSessionSynchronous', + NOT_SEEING_PREFERRED_SHOP: 'notSeeingPreferredShop' }); export default routerParams; diff --git a/src/router/router-constants/routing-table.js b/src/router/router-constants/routing-table.js index 9ee532d5..124ff62e 100644 --- a/src/router/router-constants/routing-table.js +++ b/src/router/router-constants/routing-table.js @@ -115,7 +115,7 @@ const routingTable = () => [ destinationIssPageValue: issPageValues.VEHICLE_LOOKUP }, { - scenario: issPageValues.BAILOUT_PAGE, + scenario: issPageValues.BAILOUT_PAGE, // TODO is this a bug destinationIssPageValue: issPageValues.BAILOUT_PAGE }, { @@ -666,6 +666,10 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD, destinationIssPageValue: issPageValues.TPA_SUBMIT + }, + { + scenario: navigationScenarios.CLICKED_DO_NOT_SEE_MY_SHOP_LINK, + destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, diff --git a/src/store/index.js b/src/store/index.js index 55739046..71c5d5db 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -106,6 +106,7 @@ const getDefaultState = () => ({ zipCodeCtu: null, appointmentType: null, isVehicleProtected: null, + IsSafeliteProvider: null, provider: { providerNumber: null, address: { @@ -417,12 +418,12 @@ export const useMainStore = defineStore({ order.customer.address.streetAddress = insured?.address; order.customer.address.city = insured?.city; order.customer.address.state = insured?.state; - order.customer.address.zipCode = insured?.zipCode; + order.customer.address.zipCode = insured?.zipCode?.toString(); order.customer.firstName = insured?.firstName; order.customer.lastName = insured?.lastName; // populate additional fields - order.serviceLocation.zipCode = insured?.zipCode; + order.serviceLocation.zipCode = insured?.zipCode?.toString(); order.policy.policyData = responsePolicy.policyData; // populate vehicles @@ -881,6 +882,43 @@ export const useMainStore = defineStore({ }); }, + // TODO what info is returned here + getTpaProviders(zipCode, radius) { + return new Promise((resolve, reject) => { + if (radius === 25) { + resolve({}); + } else if (radius === 50) { + resolve({ + data: [ + { + buttonLabel: 'USA Auto Glass', + buttonLabelSubCopy: '1.5 mi', + buttonBodyCopy: '760 Dearborn Park Ln, Worthington, OH 43085614-123-5555', + value: '000123' + } + ] + }); + } else { + resolve({ + data: [ + { + buttonLabel: 'USA Auto Glass', + buttonLabelSubCopy: '1.5 mi', + buttonBodyCopy: '760 Dearborn Park Ln, Worthington, OH 43085614-123-5555', + value: '000123' + }, + { + buttonLabel: 'USA Auto Glass', + buttonLabelSubCopy: '1.5 mi', + buttonBodyCopy: '760 Dearborn Park Ln, Worthington, OH 43085614-123-5555', + value: '000123' + } + ] + }); + } + }); + }, + async getSupportingItems() { const glassPartsArray = this.order.lineItems.glassParts ?? []; const { carId } = this.order.vehicle; @@ -1054,7 +1092,9 @@ export const useMainStore = defineStore({ noCoverage: policy.noCoverage, policyLookupSuccessful: policy.policyLookupSuccessful, originalDeductible: this.order.originalDeductible, - currentDeductible: this.order.currentDeductible + currentDeductible: this.order.currentDeductible, + IsItac: this.order.policy.isITAC, + OemEndorsement: policy.endorsements?.indexOf('OEM Approved') !== -1 ?? false }, customer: { address: { @@ -1062,7 +1102,7 @@ export const useMainStore = defineStore({ streetAddress2: customer.address?.streetAddress2, city: customer.address?.city, state: customer.address?.state, - zipCode: customer.address?.zipCode + zipCode: customer.address?.zipCode?.toString() }, emailAddress: contactInfo.emailAddress, firstName: contactInfo.firstName || customer.firstName, @@ -1096,6 +1136,7 @@ export const useMainStore = defineStore({ ? AppointmentTypeStrings.MOBILE : serviceLocation.appointmentType, isVehicleProtected: serviceLocation.isVehicleProtected, provider: { + IsSafeliteProvider: serviceLocation?.IsSafeliteProvider, providerNumber: serviceLocation.provider?.providerNumber, address: { streetAddress: serviceLocation.provider?.address?.streetAddress, @@ -1592,6 +1633,9 @@ export const useMainStore = defineStore({ updatePolicyITACFlag(isITAC) { this.order.policy.isITAC = isITAC; }, + updateIsSafeliteProvider(isSafelite) { + this.order.serviceLocation.IsSafeliteProvider = isSafelite; + }, updateDeductible(finalDeductible) { this.order.currentDeductible = finalDeductible; diff --git a/src/styles/bailout-common.scss b/src/styles/bailout-common.scss index 1188eeb0..d7c68d2a 100644 --- a/src/styles/bailout-common.scss +++ b/src/styles/bailout-common.scss @@ -1,4 +1,5 @@ $page-side-padding: 1.5rem; +$font-size: 0.875rem; .page-container-grouped-styles { > div.main-content-container { @@ -19,13 +20,14 @@ $page-side-padding: 1.5rem; p { line-height: 1.5rem; + font-size: $font-size; &:last-child { margin-bottom: 0; } } span { - font-size: 0.875rem; + font-size: $font-size; } } } diff --git a/src/styles/ux-variables-svg-strings.scss b/src/styles/ux-variables-svg-strings.scss index f4f28829..e99e2845 100644 --- a/src/styles/ux-variables-svg-strings.scss +++ b/src/styles/ux-variables-svg-strings.scss @@ -3,4 +3,7 @@ $svg-date-picker-forward-button: "data:image/svg+xml,%3Csvg width='7' height='12 $svg-drop-off-alert: "data:image/svg+xml,%3Csvg viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_13957_112512)'%3E%3Cpath d='M5.99865 0C4.81147 4.82643e-07 3.65095 0.352111 2.66392 1.01179C1.67688 1.67146 0.907678 2.60907 0.45361 3.70599C-0.000459241 4.80291 -0.11899 6.00986 0.113013 7.17415C0.345015 8.33845 0.917126 9.40778 1.75697 10.2469C2.59682 11.086 3.66666 11.6571 4.83117 11.8881C5.99567 12.119 7.20251 11.9994 8.29902 11.5443C9.39553 11.0893 10.3324 10.3192 10.9912 9.33159C11.65 8.34396 12.0011 7.18313 12 5.99594C11.9971 4.40566 11.3638 2.88142 10.2388 1.75742C9.11375 0.633431 7.58894 0.00143011 5.99865 0V0ZM5.99865 11.2478C4.96135 11.2473 3.94748 10.9392 3.08518 10.3627C2.22288 9.7861 1.55085 8.96685 1.15401 8.00846C0.75718 7.05006 0.653353 5.99554 0.855656 4.97815C1.05796 3.96077 1.55731 3.02618 2.29061 2.29251C3.0239 1.55884 3.95823 1.059 4.97551 0.856176C5.99279 0.653349 7.04737 0.756633 8.00597 1.15297C8.96457 1.54931 9.78416 2.22092 10.3612 3.08293C10.9382 3.94493 11.2467 4.95864 11.2478 5.99594C11.2478 7.38835 10.6949 8.72377 9.71053 9.70861C8.7262 10.6934 7.39106 11.2471 5.99865 11.2478V11.2478Z' fill='%2306577C'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M6.22736 8.84695C6.30613 8.76818 6.35038 8.66135 6.35038 8.54996V5.30996C6.35038 5.19857 6.30613 5.09174 6.22736 5.01298C6.1486 4.93421 6.04177 4.88996 5.93038 4.88996C5.81899 4.88996 5.71216 4.93421 5.63339 5.01298C5.55463 5.09174 5.51038 5.19857 5.51038 5.30996V8.54996C5.51038 8.66135 5.55463 8.76818 5.63339 8.84695C5.71216 8.92571 5.81899 8.96996 5.93038 8.96996C6.04177 8.96996 6.1486 8.92571 6.22736 8.84695ZM5.69704 3.97918C5.76611 4.02533 5.84731 4.04996 5.93038 4.04996C5.98558 4.05012 6.04026 4.03936 6.09129 4.01831C6.14232 3.99726 6.18868 3.96633 6.22771 3.9273C6.26675 3.88827 6.29768 3.8419 6.31873 3.79088C6.33978 3.73985 6.35053 3.68516 6.35038 3.62996C6.35038 3.54689 6.32574 3.46569 6.27959 3.39662C6.23344 3.32755 6.16785 3.27372 6.0911 3.24193C6.01436 3.21014 5.92991 3.20183 5.84844 3.21803C5.76697 3.23424 5.69213 3.27424 5.63339 3.33298C5.57465 3.39171 5.53465 3.46655 5.51845 3.54802C5.50224 3.6295 5.51056 3.71394 5.54235 3.79069C5.57414 3.86743 5.62797 3.93303 5.69704 3.97918Z' fill='%2306577C'/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_13957_112512'%3E%3Crect width='12' height='12' fill='white'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A"; $svg-shop-list-button-green-availability: "data:image/svg+xml,%3Csvg viewBox='0 0 13 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.5 12C3.19159 12 0.5 9.30841 0.5 6C0.5 2.69159 3.19159 0 6.5 0C9.80841 0 12.5 2.69159 12.5 6C12.5 9.30841 9.80841 12 6.5 12ZM6.5 0.785047C3.62449 0.785047 1.28505 3.12449 1.28505 6C1.28505 8.87551 3.62449 11.215 6.5 11.215C9.37551 11.215 11.715 8.87551 11.715 6C11.715 3.12449 9.37551 0.785047 6.5 0.785047Z' fill='%23006A36'/%3E%3Cpath d='M5.697 7.95252C5.5927 7.95252 5.49289 7.91102 5.41999 7.837L3.90597 6.32299C3.75233 6.16934 3.75233 5.92149 3.90597 5.76785C4.05962 5.6142 4.30747 5.6142 4.46111 5.76785L5.69812 7.00373L8.53999 4.16186C8.69364 4.00822 8.94149 4.00822 9.09513 4.16186C9.24878 4.31551 9.24878 4.56336 9.09513 4.717L5.97626 7.83588C5.90224 7.9099 5.80242 7.9514 5.69925 7.9514L5.697 7.95252Z' fill='%23006A36'/%3E%3C/svg%3E%0A"; $svg-shop-list-button-orange-availability: "data:image/svg+xml,%3Csvg viewBox='0 0 13 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.5 0C9.81368 0 12.5 2.68632 12.5 6C12.5 9.31368 9.81368 12 6.5 12C3.18632 12 0.5 9.31368 0.5 6C0.5 2.68632 3.18632 0 6.5 0ZM6.5 0.84C3.6548 0.84 1.34 3.1548 1.34 6C1.34 8.8452 3.6548 11.16 6.5 11.16C9.3452 11.16 11.66 8.8452 11.66 6C11.66 3.1548 9.3452 0.84 6.5 0.84ZM7.90018 4.00596C8.06422 3.84204 8.33002 3.84192 8.49406 4.00596C8.6581 4.17 8.6581 4.43592 8.49406 4.59996L7.0939 6L8.49406 7.40004C8.6581 7.56408 8.6581 7.83 8.49406 7.99404C8.4121 8.076 8.30458 8.11704 8.19706 8.11704C8.08966 8.11704 7.98214 8.076 7.90018 7.99404L6.50002 6.594L5.09986 7.99404C5.01778 8.076 4.91038 8.11704 4.80286 8.11704C4.69546 8.11704 4.58794 8.076 4.50598 7.99404C4.34182 7.83 4.34182 7.56408 4.50598 7.40004L5.90614 6L4.50598 4.59996C4.34182 4.43592 4.34182 4.17 4.50598 4.00596C4.66978 3.84192 4.93582 3.84204 5.09986 4.00596L6.50002 5.406L7.90018 4.00596Z' fill='%23E86421'/%3E%3C/svg%3E%0A"; -$svg-update-zip-text-link: "data:image/svg+xml,%3Csvg viewBox='0 0 13 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.49635 1.00142e-07C5.64734 -0.000153295 4.80722 0.175918 4.0274 0.517444C3.24757 0.858969 2.54443 1.35877 1.96099 1.98626C0.765713 3.27588 0.0999756 4.98141 0.0999756 6.75394C0.0999756 8.52646 0.765713 10.232 1.96099 11.5216L5.98324 15.777C6.04954 15.8475 6.12918 15.9036 6.21736 15.9419C6.30555 15.9802 6.40045 16 6.49635 16C6.59225 16 6.68716 15.9802 6.77534 15.9419C6.86353 15.9036 6.94317 15.8475 7.00946 15.777L11.0317 11.52C12.2391 10.2383 12.909 8.52914 12.8999 6.75394C12.9094 4.97818 12.2394 3.26832 11.0317 1.98626C10.4481 1.35899 9.74493 0.859347 8.96514 0.517839C8.18535 0.176331 7.34532 0.000130509 6.49635 1.00142e-07V1.00142e-07ZM6.49635 9.13131C6.02507 9.13131 5.56437 8.98913 5.17251 8.72275C4.78065 8.45637 4.47524 8.07776 4.29488 7.63479C4.11453 7.19181 4.06734 6.70438 4.15928 6.23412C4.25123 5.76387 4.47817 5.33191 4.81142 4.99287C5.14467 4.65384 5.56925 4.42295 6.03148 4.32941C6.49371 4.23587 6.97282 4.28388 7.40823 4.46736C7.84364 4.65085 8.21579 4.96157 8.47762 5.36023C8.73945 5.7589 8.87921 6.2276 8.87921 6.70707C8.87921 7.34974 8.62837 7.96611 8.18185 8.4207C7.73532 8.87528 7.12964 9.13088 6.49794 9.13131H6.49635Z' fill='%231574A1'/%3E%3C/svg%3E%0A"; \ No newline at end of file +$svg-update-zip-text-link: "data:image/svg+xml,%3Csvg viewBox='0 0 13 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.49635 1.00142e-07C5.64734 -0.000153295 4.80722 0.175918 4.0274 0.517444C3.24757 0.858969 2.54443 1.35877 1.96099 1.98626C0.765713 3.27588 0.0999756 4.98141 0.0999756 6.75394C0.0999756 8.52646 0.765713 10.232 1.96099 11.5216L5.98324 15.777C6.04954 15.8475 6.12918 15.9036 6.21736 15.9419C6.30555 15.9802 6.40045 16 6.49635 16C6.59225 16 6.68716 15.9802 6.77534 15.9419C6.86353 15.9036 6.94317 15.8475 7.00946 15.777L11.0317 11.52C12.2391 10.2383 12.909 8.52914 12.8999 6.75394C12.9094 4.97818 12.2394 3.26832 11.0317 1.98626C10.4481 1.35899 9.74493 0.859347 8.96514 0.517839C8.18535 0.176331 7.34532 0.000130509 6.49635 1.00142e-07V1.00142e-07ZM6.49635 9.13131C6.02507 9.13131 5.56437 8.98913 5.17251 8.72275C4.78065 8.45637 4.47524 8.07776 4.29488 7.63479C4.11453 7.19181 4.06734 6.70438 4.15928 6.23412C4.25123 5.76387 4.47817 5.33191 4.81142 4.99287C5.14467 4.65384 5.56925 4.42295 6.03148 4.32941C6.49371 4.23587 6.97282 4.28388 7.40823 4.46736C7.84364 4.65085 8.21579 4.96157 8.47762 5.36023C8.73945 5.7589 8.87921 6.2276 8.87921 6.70707C8.87921 7.34974 8.62837 7.96611 8.18185 8.4207C7.73532 8.87528 7.12964 9.13088 6.49794 9.13131H6.49635Z' fill='%231574A1'/%3E%3C/svg%3E%0A"; +$svg-search-icon: "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='M15.7817 14.7328L11.8252 10.7762C12.8833 9.45005 13.3936 7.76911 13.2513 6.07849C13.1091 4.38788 12.325 2.81587 11.0601 1.6852C9.79515 0.554524 8.14538 -0.0490261 6.44946 -0.00154744C4.75353 0.0459312 3.14012 0.740836 1.94045 1.94051C0.740775 3.14018 0.0458701 4.75359 -0.00160848 6.44952C-0.0490871 8.14545 0.554463 9.79521 1.68514 11.0601C2.81581 12.325 4.38782 13.1091 6.07843 13.2514C7.76905 13.3937 9.44999 12.8834 10.7762 11.8252L14.7349 15.7839C14.8044 15.8527 14.8869 15.907 14.9774 15.9439C15.068 15.9808 15.165 15.9995 15.2628 15.9989C15.3606 15.9983 15.4573 15.9784 15.5475 15.9405C15.6376 15.9025 15.7194 15.8471 15.7881 15.7776C15.8568 15.708 15.9112 15.6256 15.9481 15.535C15.985 15.4444 16.0036 15.3474 16.0031 15.2496C16.0025 15.1518 15.9826 15.0551 15.9446 14.965C15.9067 14.8748 15.8513 14.7931 15.7817 14.7243V14.7328ZM6.63737 11.7913C5.61803 11.7913 4.62157 11.4891 3.77402 10.9228C2.92646 10.3564 2.26587 9.5515 1.87578 8.60975C1.4857 7.668 1.38363 6.63172 1.5825 5.63196C1.78136 4.6322 2.27222 3.71386 2.99301 2.99307C3.7138 2.27229 4.63214 1.78142 5.6319 1.58256C6.63166 1.38369 7.66793 1.48576 8.60969 1.87585C9.55144 2.26593 10.3564 2.92652 10.9227 3.77408C11.489 4.62163 11.7913 5.61809 11.7913 6.63743C11.7896 8.00382 11.2461 9.31376 10.2799 10.2799C9.3137 11.2461 8.00376 11.7897 6.63737 11.7913Z' fill='%231574A1'/%3E%3C/svg%3E%0A"; +$svg-select-icon: "data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 8.89' xml:space='preserve'%3e%3cpath d='M8 8.89c-.24 0-.46-.09-.63-.26L.26 1.53a.901.901 0 0 1 0-1.27C.43.1.66 0 .9 0s.47.1.64.26L8 6.74 14.47.27c.17-.17.4-.27.64-.27s.47.1.63.27c.17.17.26.4.26.64s-.1.47-.27.63l-7.1 7.09a.86.86 0 0 1-.63.26z' fill='%231474a2'/%3e%3c/svg%3e"; +$svg-calendar-picker: "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"; \ No newline at end of file diff --git a/src/ux-components/modal-button-main/modal-button-main.vue b/src/ux-components/modal-button-main/modal-button-main.vue index d76af58c..ff826f65 100644 --- a/src/ux-components/modal-button-main/modal-button-main.vue +++ b/src/ux-components/modal-button-main/modal-button-main.vue @@ -2,7 +2,7 @@