Merge pull request #41 from Safelite/features/funnel-transitions

Features/funnel transitions
This commit is contained in:
max-dempsey 2021-11-15 16:33:33 -05:00 committed by GitHub
commit c8bb13a540
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 581 additions and 349 deletions

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 45.6 36.1" xml:space="preserve">
<path d="M43.6 0H18.1c-.4 0-.8.1-1.1.4l-.3.3-16 16c-.8.8-.8 2 0 2.8l16 16 .3.3c.3.2.7.4 1.1.4h25.5c1.1 0 2-.9 2-2V2c0-1.1-.9-2-2-2zm-3.4 28.1-1.4 1.4-10-10-10 10-1.4-1.4 10-10-10-10 1.4-1.4 10 10 10-10L40.2 8l-10 10 10 10.1z" fill="#8e9292"/>
</svg>

After

Width:  |  Height:  |  Size: 338 B

View file

@ -1,6 +0,0 @@
describe("baseTemplate.vue", () => {
it("Is a test", () => {
expect(true).toBe(true);
});
});

View file

@ -1,56 +0,0 @@
<template>
<div class="select-car sticky-lg-top py-4">
<div class="select-car-form overflow-hidden p-3 rounded">
<div class="car_info text-center">
<div class="car-image mb-3">
<img
class="vehicle-image img-fluid"
:class="{ blur: blurImg }"
:src="carImg"
/>
</div>
<div class="current_car_info">
<transition appear name="fade">
<div
class="current_car_info-text"
v-on:click="backFunction ? backFunction() : null"
>
<span class="text-center text-secondary fs-5 d-block">{{
this.currentCarInfo
}}</span>
<span v-if="style" class="text-center text-secondary fs-6 mt-2">{{
style
}}</span>
</div>
</transition>
</div>
<transition appear :name="slideTransition">
<div class="needed_car_info d-flex overflow-hidden mt-4 mb-3">
<span class="text-center fs-6 fw-bold needed_car_info-text">{{
this.neededCarInfo
}}</span>
</div>
</transition>
</div>
<transition appear :name="slideTransition">
<slot></slot>
</transition>
</div>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "BaseTemplate",
computed: {
...mapState(["slideTransition", "carImg", "blurImg", "style"]),
},
props: {
currentCarInfo: String,
neededCarInfo: String,
backFunction: Function,
},
};
</script>

View file

@ -1 +0,0 @@
test.todo('some test to be written in the future');

View file

@ -1,24 +0,0 @@
<template>
<form class="list-view d-flex">
<div class="car_list">
<div v-for="attritbute in attritbutes" :key="attritbute" class="mb-2">
<buttonPrimary :buttonText="attritbute" v-on:click="selectAttribute(attritbute)" buttonType="btn-funnel" />
</div>
</div>
</form>
</template>
<script>
import buttonPrimary from "@/uxComponents/buttonPrimary/buttonPrimary";
export default {
name: "CarAttributes",
props: {
attritbutes: Array,
selectAttribute: Function,
},
components: {
buttonPrimary,
},
};
</script>

View file

@ -0,0 +1,22 @@
import { shallowMount } from '@vue/test-utils';
import radioQuestion from './radioQuestion';
describe('radioQuestion.vue', () => {
it("Should render the 'questionText' prop value as a span value for the radio question and the 'answer' values should render as text values for radio components.", () => {
// Act
const wrapper = shallowMount(radioQuestion, {
propsData: {
questionText: 'Question Text',
answers: ['2023', '2022', '2021'],
chooseAnswer: function(test){ console.log(test) }
}
});
// Assert
expect(wrapper.find('.needed_car_info-text').text()).toEqual('Question Text');
const radioButtons = wrapper.findAllComponents('[data-test="radio"]');
expect(radioButtons[0].attributes('text')).toEqual('2023');
expect(radioButtons[2].attributes('text')).toEqual('2021');
expect(typeof wrapper.props().chooseAnswer).toBe('function');
})
})

View file

@ -0,0 +1,27 @@
<template>
<div class="radio_question">
<div class="needed_car_info d-flex mt-4 mb-3">
<span class="text-center fs-6 fw-bold w-100 needed_car_info-text">{{ questionText }}</span>
</div>
<div class="car_list overflow-scroll position-absolute">
<div v-for="answer in answers" :key="answer" class="mb-2">
<radio :text="answer" :value="answer" @click="chooseAnswer(answer)" data-test="radio" />
</div>
</div>
</div>
</template>
<script>
import radio from "@/uxComponents/radio/radio";
export default {
name: "radio-question",
props: {
questionText: String,
answers: Array,
chooseAnswer: Function
},
components: {
radio
}
};
</script>

View file

@ -2,6 +2,25 @@ const endpoints = {
GetRouteInfoEndpoint: {
url: '/content/api/v1/content/RouteInfo',
method: 'POST'
},
GetYears: {
url: '/vehicle/api/v1/vehicle/years',
method: 'GET'
},
GetMakes: {
url: '/vehicle/api/v1/vehicle/Makes',
method: 'POST'
},
GetModels: {
url: '/vehicle/api/v1/vehicle/Models',
method: 'POST'
},
GetStyles: {
url: '/vehicle/api/v1/vehicle/Styles',
method: 'POST'
},
GetPageData: {
method: 'GET'
}
}

View file

@ -1,5 +1,7 @@
const storeActions = {
GET_ROUTE_INFO_ACTION: "getRouteInfo",
GET_YEARS: 'getYears',
GET_PAGE_DATA: 'getMockPageData',
}
export { storeActions }

View file

@ -11,7 +11,7 @@ export default {
axios({
method: method,
url: `${apiGatewayUrl}${endpoint}`,
url: apiGatewayUrl + endpoint,
data: payloadAndAnalyticsData,
crossDomain: true,
responseType: {},
@ -29,4 +29,30 @@ export default {
);
});
},
/* istanbul ignore next */
callMockHttpClient({ method, endpoint}) {
// For Mock use only!
return new Promise((resolve, reject) => {
axios({
method: method,
url: endpoint,
crossDomain: true,
responseType: {},
}).then((response) => {
if (response.status == httpStatusCodes.OK) {
resolve(response);
} else {
reject(response);
}
},
(error) => {
return reject(error.response);
}
);
});
}
};

View file

@ -0,0 +1,73 @@
import globalMethods from "@/global-methods";
import axios from 'axios';
//Mock external dependencies
jest.mock('axios');
it("Global Methods - Call Http Client - Should Resolve Promise", () => {
//Arrange
const endpoint = 'https://mock.safelite.com';
const httpArgs = setupMocksForHttpClient({ endpoint: endpoint });
//Act
globalMethods.callHttpClient(httpArgs)
.then((response) => {
//Assert
expect(axios.mock.calls[0][0].url).toContain(endpoint);
expect(response.data.message).toContain('Success');
expect(response.status).toEqual(200);
});
});
it("Global Methods - Call Http Client - Should Reject Promise", () => {
//Arrange
const endpoint = 'https://mock.safelite.com';
const httpArgs = setupMocksForHttpClient({ endpoint: endpoint, isError: true });
//Act
globalMethods.callHttpClient(httpArgs)
.catch((err) => {
//Assert
expect(axios.mock.calls[0][0].url).toContain(endpoint);
expect(err.data.message).toContain('Error');
expect(err.status).toEqual(500);
});
});
function setupMocksForHttpClient({ endpoint = null, isError = false, additionalData = null }) {
//Clear node module
axios.mockClear();
// Success Response
const response = {
status: 200,
data: {
message: 'Success',
additionalData: additionalData
}
};
// Error Response
const error = {
response: {
status: 500,
data: {
message: 'Error',
additionalData: additionalData
}
}
}
// Error interceptor on Axios returns a different object, so we need to mimic that.
if (isError) {
axios.mockRejectedValue(error);
} else {
axios.mockResolvedValue(response);
}
return {
endpoint: endpoint
}
}

View file

@ -0,0 +1,26 @@
import { storeActions } from "@/constants/storeActions";
export function getMountOptions(mockData) {
// Define our mocks to attached to the 'global' object for Vue/Jest.
const mocks = {};
mocks.dispatchNonBlockingStoreAction = jest.fn();
mocks.dispatchNonBlockingStoreAction.mockImplementation((actionName) => {
let actionFilterResult = mockData.actionList.filter(x => x.actionName == actionName);
if (actionFilterResult.length > 0 && actionFilterResult.length === 1) {
return Promise.resolve({
data: actionFilterResult[0].data
});
}
});
// Mock store actions from js file
mocks.storeActions = storeActions;
const global = {
mocks: mocks
};
return { global }
}

View file

@ -112,6 +112,16 @@
<h6>h6 Heading</h6>
</div>
</div>
<div class="my-3">
<div class="select-car">
<div class="select-car-form">
<radioQuestion
questionText="What year is your vehicle?"
:answers="this.years"
/>
</div>
</div>
</div>
</div>
</template>
@ -130,5 +140,10 @@ export default {
listButton,
radioList
},
data() {
return {
years: [2023, 2022, 2021, 2020],
};
}
};
</script>

View file

@ -1 +0,0 @@
test.todo('some test to be written in the future');

View file

@ -1,77 +0,0 @@
<template>
<div>
<baseTemplate
:currentCarInfo="year + ' ' + make + ' ' + model"
neededCarInfo="What is your damage?"
>
<div class="damage-section text-center">
<div class="choose-damage row">
<div class="col">
<radioCard />
</div>
<div class="col">
<radioCard />
</div>
<div class="col">
<radioCard />
</div>
</div>
<transition name="fade">
<div v-if="damage" class="how-bad mt-3">
<span class="text-center fs-6 fw-bold">How bad is it?</span>
<div class="choose-how-bad row mt-2">
<div class="col">
<radioCard />
</div>
<div class="col">
<radioCard />
</div>
</div>
</div>
</transition>
<div class="next-action row">
<div class="col">
<div v-on:click="goBack" class="change">
<span>Change Vehicle</span>
</div>
</div>
</div>
</div>
</baseTemplate>
</div>
</template>
<script>
import baseTemplate from "@/commonComponents/baseTemplate/baseTemplate";
import radioCard from "@/uxComponents/radioCard/radioCard";
import { mapState } from "vuex";
export default {
name: "SelectCar",
computed: {
...mapState(["year", "make", "model"]),
},
data() {
return {
damage: "",
};
},
methods: {
chooseDamage(damage) {
this.damage = damage;
console.log(this.damage);
},
goBack() {
this.$store.state.slideTransition = "slide_reverse";
this.$store.commit("removeStyle");
this.$router.push(
`/select-style?year=${this.$store.state.year}&make=${this.$store.state.make}`
);
},
},
components: {
baseTemplate,
radioCard,
},
};
</script>

View file

@ -1,14 +1,23 @@
<template>
<div>
<baseTemplate :currentCarInfo="year" neededCarInfo="What make is your vehicle?" :backFunction="goBack">
<carAttributes :attritbutes="makes" :selectAttribute="selectMake" />
</baseTemplate>
</div>
<div class="select-car overflow-hidden">
<div class="select-car-form rounded text-center">
<Header
:text="year"
/>
<radioQuestion
questionText="What is the make of your vehicle?"
:answers="makes"
:chooseAnswer="selectMake"
/>
</div>
</div>
</template>
<script>
import baseTemplate from "@/commonComponents/baseTemplate/baseTemplate";
import carAttributes from "@/commonComponents/carAttributes/carAttributes";
import radioQuestion from "@/commonComponents/radioQuestion/radioQuestion";
import Header from "@/uxComponents/header/header";
import store from "@/store";
import router from "@/router";
import {
mapState
} from "vuex";
@ -20,7 +29,11 @@ export default {
},
methods: {
selectMake(make) {
this.$store.dispatch("selectMake", make);
this.dispatchNonBlockingStoreAction("selectMake", { make: make }).then( (response) => {
const models = response;
store.commit('updateMake', {make, models});
router.push(`/select-model?year=${store.year}&make=${make}`);
});
},
goBack() {
this.$store.state.slideTransition = "slide_reverse";
@ -28,8 +41,8 @@ export default {
},
},
components: {
baseTemplate,
carAttributes,
radioQuestion,
Header,
},
};
</script>

View file

@ -1,18 +1,23 @@
<template>
<div>
<baseTemplate
:currentCarInfo="year + ' ' + make"
:neededCarInfo="'What is the model of your ' + make + '?'"
:backFunction="goBack"
>
<carAttributes :attritbutes="models" :selectAttribute="selectModel" />
</baseTemplate>
<div class="select-car overflow-hidden">
<div class="select-car-form rounded text-center">
<Header
:text="year + ' ' + make"
/>
<radioQuestion
:questionText="'What is the model of your ' + make + '?'"
:answers="models"
:chooseAnswer="selectModel"
/>
</div>
</div>
</template>
<script>
import baseTemplate from "@/commonComponents/baseTemplate/baseTemplate";
import carAttributes from "@/commonComponents/carAttributes/carAttributes";
import radioQuestion from "@/commonComponents/radioQuestion/radioQuestion";
import Header from "@/uxComponents/header/header";
import store from "@/store";
import router from "@/router";
import { mapState } from "vuex";
export default {
@ -22,16 +27,20 @@ export default {
},
methods: {
selectModel(model) {
this.$store.dispatch("selectModel", model);
this.dispatchNonBlockingStoreAction("selectModel", { model: model }).then( (response) => {
const styles = response;
store.commit("updateModel", { model, styles });
router.push(`/select-style?year=${store.year}&make=${store.make}&model=${model}`);
});
},
goBack() {
this.$store.state.slideTransition = "slide_reverse";
this.$router.push(`/select-make?year=${this.$store.state.year}`);
this.$router.push(`/select-make?year=${store.year}`);
},
},
components: {
baseTemplate,
carAttributes,
radioQuestion,
Header,
},
};
</script>

View file

@ -1,18 +1,23 @@
<template>
<div>
<baseTemplate
:currentCarInfo="year + ' ' + make + ' ' + model"
:neededCarInfo="'What style is your ' + model + '?'"
:backFunction="goBack"
>
<carAttributes :attritbutes="styles" :selectAttribute="selectStyle" />
</baseTemplate>
<div class="select-car overflow-hidden">
<div class="select-car-form rounded text-center">
<Header
:text="year + ' ' + make + ' ' + model"
/>
<radioQuestion
:questionText="'What style is your ' + model + '?'"
:answers="styles"
:chooseAnswer="selectStyle"
/>
</div>
</div>
</template>
<script>
import baseTemplate from "@/commonComponents/baseTemplate/baseTemplate";
import carAttributes from "@/commonComponents/carAttributes/carAttributes";
import radioQuestion from "@/commonComponents/radioQuestion/radioQuestion";
import Header from "@/uxComponents/header/header";
import store from "@/store";
import router from "@/router";
import { mapState } from "vuex";
export default {
@ -22,18 +27,21 @@ export default {
},
methods: {
selectStyle(style) {
this.$store.dispatch("selectStyle", style);
store.commit("updateStyle", { style });
router.push(
`/select-damage?year=${store.year}&make=${store.make}&model=${store.model}&style=${style}`
);
},
goBack() {
this.$store.state.slideTransition = "slide_reverse";
this.$router.push(
`/select-model?year=${this.$store.state.year}&make=${this.$store.state.make}`
`/select-model?year=${store.year}&make=${store.make}`
);
},
},
components: {
baseTemplate,
carAttributes,
radioQuestion,
Header,
},
};
</script>

View file

@ -1 +1,55 @@
test.todo('some test to be written in the future');
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue'
import { getMountOptions } from '@/helpers/unitTestHelper.js';
import { storeActions } from '@/constants/storeActions.js'
import selectYear from './selectYear.vue';
describe('selectYear.vue', () => {
test("Should render the 'pageHeader.Text' data value 'text' prop value for the Header component, 'radioQuestion.Question' data value as 'questionText' prop value for the 'radioQuestion' component and 'years' values for 'answers' prop values for the 'radioQuestion' component.", async () => {
// Arrange
const mockDataAndAction = {
actionList: [
{
actionName: storeActions.GET_PAGE_DATA,
data: {
"Result": [
{
Type: "PageHeadingWidget",
Model: {
Text: "Mock Data"
}
},
{
Type: "RadioQuestionWidget",
Model: {
Question: "Mock Data"
}
}
]
}
},
{
actionName: storeActions.GET_YEARS,
data: ['2023', '2022', '2021']
}
]
};
const mountOptions = getMountOptions(mockDataAndAction);
// Act
const wrapper = shallowMount(selectYear, mountOptions);
await nextTick()
// Assert
const header = await wrapper.find('.Header');
expect(header.attributes('text')).toEqual("Mock Data");
const radioQuestion = await wrapper.findComponent('.radioQuestion');
expect(radioQuestion.attributes('answers')).toEqual('2023,2022,2021');
expect(radioQuestion.attributes('questiontext')).toEqual("Mock Data");
});
})

View file

@ -1,60 +1,78 @@
<template>
<div>
<baseTemplate :currentCarInfo="pageHeader.Text" :neededCarInfo="radioQuestion.Question">
<carAttributes :attritbutes="years" :selectAttribute="selectYear" />
</baseTemplate>
</div>
<div class="select-car">
<div class="select-car-form rounded text-center">
<Header
:text="pageHeader.Text" class="Header"
/>
<radioQuestion
class="radioQuestion"
:questionText="radioQuestion.Question"
:answers="this.years"
:chooseAnswer="selectYear"
/>
</div>
</div>
</template>
<script>
import baseTemplate from "@/commonComponents/baseTemplate/baseTemplate";
import carAttributes from "@/commonComponents/carAttributes/carAttributes";
import axios from "axios";
import radioQuestion from "@/commonComponents/radioQuestion/radioQuestion";
import Header from "@/uxComponents/header/header";
import store from "@/store";
import router from "@/router";
import { mapState } from "vuex";
export default {
name: "SelectCar",
data() {
return {
years: [],
pageHeader: {},
radioQuestion: {}
};
},
mounted() {
axios.get("https://mockey.qa.sagaws.net/service/years").then((response) => {
this.years = response.data.Result;
});
name: "SelectYear",
data() {
return {
years: [],
pageHeader: {},
radioQuestion: {}
};
},
computed: {
...mapState(["slideTransition", "carImg", "blurImg", "style"]),
},
mounted() {
// Mocking Sitefinity data to (For Page Header and Radio Question)
this.dispatchNonBlockingStoreAction(this.storeActions.GET_PAGE_DATA, {relativeUrl: 'https://mockey.qa.sagaws.net/service/content/selectYear'})
.then( (response) => {
response.data.Result.forEach((m) => {
switch (m.Type) {
case 'PageHeadingWidget': {
this.pageHeader = m.Model;
break;
}
case 'RadioQuestionWidget': {
this.radioQuestion = m.Model;
break;
}
default: {
break;
}
}
})
});
// Sample Call to Mockey to represent real data being sent back from the API, and how the modles are supposed to
// be prosessed and passed to child components.
axios.get("https://mockey.qa.sagaws.net/service/content/selectYear").then((response) => {
response.data.Result.forEach((m) => {
switch (m.Type) {
case this.widgetNames.PAGE_HEADING_WIDGET: {
this.pageHeader = m.Model;
break;
}
case this.widgetNames.RADIO_QUESTION_WIDGET: {
this.radioQuestion = m.Model;
break;
}
default: {
break;
}
}
})
});
},
methods: {
selectYear(year) {
this.$store.dispatch("selectYear", year);
},
},
components: {
baseTemplate,
carAttributes,
},
// Getting Years list
this.dispatchNonBlockingStoreAction(this.storeActions.GET_YEARS)
.then( (response) => {
this.years = response.data;
});
},
methods: {
selectYear(year) {
this.dispatchNonBlockingStoreAction("selectYear", { year: year }).then( (response) => {
const makes = response;
store.commit('updateYear', {year, makes});
router.push(`select-make?year=${year}`);
});
}
},
components: {
radioQuestion,
Header,
},
};
</script>

View file

@ -1,9 +1,7 @@
import { createStore } from "vuex";
import { endpoints } from "@/constants/endpoints.js";
import createPersistedState from "vuex-persistedstate";
import axios from "axios";
import globalMethods from "@/global-methods";
import router from "@/router";
export default createStore({
plugins: [
@ -27,17 +25,17 @@ export default createStore({
mutations: {
updateYear(state, data) {
state.year = data.year;
state.makes = data.makes;
state.makes = data.makes.data;
state.slideTransition = "slide";
},
updateMake(state, data) {
state.make = data.make;
state.models = data.models.data.Result;
state.models = data.models.data;
state.slideTransition = "slide";
},
updateModel(state, data) {
state.model = data.model;
state.styles = data.styles.data.Result;
state.styles = data.styles.data;
state.slideTransition = "slide";
},
updateStyle(state, data) {
@ -55,50 +53,60 @@ export default createStore({
},
},
actions: {
async selectYear(context, year) {
let makes = [];
await globalMethods
.callHttpClient({
method: "GET",
endpoint: `https://mockey.qa.sagaws.net/service/makes/year=${year}`,
payload: {},
})
.then(function (response) {
makes = response.data.Result;
getYears(){
return globalMethods.callHttpClient({
method: endpoints.GetYears.method,
endpoint: endpoints.GetYears.url,
payload:{}
});
context.commit("updateYear", { year, makes });
router.push(`/select-make?year=${year}`);
},
async selectMake({ commit, state }, make) {
const models = await axios.get(
`https://mockey.qa.sagaws.net/service/models/year=${state.year}/make=${make}`
);
commit("updateMake", { make, models });
router.push(`/select-model?year=${state.year}&make=${make}`);
},
async selectModel({ commit, state }, model) {
const styles = await axios.get(
`https://mockey.qa.sagaws.net/service/styles/year=${state.year}/make=${state.make}/model=${model}`
);
commit("updateModel", { model, styles });
router.push(
`/select-style?year=${state.year}&make=${state.make}&model=${model}`
);
},
selectStyle({ commit, state }, style) {
commit("updateStyle", { style });
router.push(
`/damage?year=${state.year}&make=${state.make}&model=${state.model}&style=${style}`
);
},
getRouteInfo(context, { pageName }) {
return globalMethods.callHttpClient({
method: endpoints.GetRouteInfoEndpoint.method,
endpoint: endpoints.GetRouteInfoEndpoint.url,
payload: {
pageName: pageName,
},
});
},
},
selectYear(context, { year }) {
return globalMethods.callHttpClient({
method: endpoints.GetMakes.method,
endpoint: endpoints.GetMakes.url,
payload:{
"Year": year
}
});
},
selectMake({ commit, state }, { make }) {
return globalMethods.callHttpClient({
method: endpoints.GetModels.method,
endpoint: endpoints.GetModels.url,
payload:{
"Year": state.year,
"Make": make
}
});
},
selectModel({ commit, state }, { model }) {
return globalMethods.callHttpClient({
method: endpoints.GetStyles.method,
endpoint: endpoints.GetStyles.url,
payload:{
"Year": state.year,
"Make": state.make,
"Model": model
}
});
},
getRouteInfo(context, { relativeUrl }) {
return globalMethods.callHttpClient({
method: endpoints.GetRouteInfoEndpoint.method,
endpoint: endpoints.GetRouteInfoEndpoint.url,
payload: {
relativeUrl: relativeUrl,
},
});
},
// For mock data purposes only
getMockPageData(context, { relativeUrl }) {
return globalMethods.callMockHttpClient({
method: endpoints.GetPageData.method,
endpoint: relativeUrl,
payload: {
},
});
},
},
});

View file

@ -1,61 +1,41 @@
.vehicle-select-attributes {
height: 100vh;
.select-car {
height: calc(100vh - 1rem);
.select-car-form {
background: $white;
height: 100%;
form {
height: calc(100% - 230px);
overflow-y: scroll;
overflow-x: hidden;
}
.car_list, .current_car_info-text, .needed_car_info-text {
flex-shrink: 0;
width: 100%;
position: relative;
transition: transform .3s;
}
}
.current_car_info {
position: relative;
min-height: 2.5rem;
.current_car_info-text {
position: absolute;
}
.car_list {
// Height will be determined by overall height of content above list
height: calc(100% - 100px);
width: calc(100% - 1rem);
}
}
// Animations
.slide-enter-from {
opacity: 0;
transform: translateX(30vw);
opacity: 0;
transform: translateX(30vw);
}
.slide-enter-active, .slide-leave-active {
transition: all .4s;
transition: all .4s;
}
.slide-leave-to {
opacity: 0;
transform: translateX(-30vw);
opacity: 0;
transform: translateX(-30vw);
}
.slide_reverse-enter-from {
opacity: 0;
transform: translateX(-30vw);
opacity: 0;
transform: translateX(-30vw);
}
.slide_reverse-enter-active, .slide_reverse-leave-active {
transition: all .4s;
transition: all .4s;
}
.slide_reverse-leave-to {
opacity: 0;
transform: translateX(30vw);
opacity: 0;
transform: translateX(30vw);
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
opacity: 0;
}
.fade-enter-active, .fade-leave-active {
transition: all .5s ease;
}
transition: all .5s ease;
}

View file

@ -14,12 +14,12 @@
export default {
name: "buttonPrimary",
props: {
buttonText: String
buttonText: String,
isDisabled: Boolean
},
data() {
return {
isLoading: false,
isDisabled: false,
isLoading: false
};
},
methods: {

View file

@ -0,0 +1,16 @@
import { shallowMount } from '@vue/test-utils';
import Header from './header';
describe('Header.vue', () => {
it("Should render the 'text' prop value as a span value for the header span text value.", () => {
// Act
const wrapper = shallowMount(Header, {
propsData: {
text: 'Header Content'
}
});
// Assert
expect(wrapper.find('span').text()).toContain("Header Content");
})
});

View file

@ -0,0 +1,16 @@
<template>
<div class="current_car_info-text">
<div class="d-flex align-items-center justify-content-center">
<span class="text-center text-dark fs-5 d-block">{{ text }}</span>
</div>
</div>
</template>
<script>
export default {
name: "Header",
props: {
text: String
}
};
</script>

View file

@ -0,0 +1,23 @@
import { shallowMount } from '@vue/test-utils';
import radio from './radio';
describe('radio.vue', () => {
it("Should render the 'text' prop value as a span value for the radio button label and add the 'value' prop value as the radio button value and id.", () => {
// Act
const wrapper = shallowMount(radio, {
propsData: {
value: '2023',
text: '12'
}
});
// Assert
expect(wrapper.find('span').text()).toEqual('12');
expect(wrapper.find('input').attributes()).toEqual({
class: 'position-absolute opacity-0',
id: '2023',
type: 'radio',
value: '2023'
});
})
})

View file

@ -0,0 +1,39 @@
<template>
<div>
<input
type="radio"
:id="this.value"
:value="this.value"
class="position-absolute opacity-0"
v-on:click="showLoader()"
/>
<label
:for="this.value"
class="btn list-button d-flex align-items-center"
v-bind:class="[this.isLoading ? 'button-loader' : '']"
>
<span>{{ text }}</span>
</label>
</div>
</template>
<script>
export default {
name: "radio",
props: [
"value",
"text"
],
data() {
return {
isLoading: false,
};
},
methods: {
showLoader() {
this.isLoading = true;
},
},
};
</script>