Visual tweaks + combine selection between components
This commit is contained in:
parent
85b0b65ca2
commit
725b504d67
6 changed files with 107 additions and 61 deletions
3
src/assets/img/icons/chevron-no-background.svg
Normal file
3
src/assets/img/icons/chevron-no-background.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.00161 3.1177C7.76595 3.11605 7.53923 3.2078 7.37109 3.37286L0.264342 10.4688C0.0950866 10.638 -1.78339e-09 10.8674 0 11.1067C1.78339e-09 11.3459 0.0950866 11.5754 0.264342 11.7446C0.433597 11.9137 0.663157 12.0088 0.90252 12.0088C1.14188 12.0088 1.37144 11.9137 1.5407 11.7446L8.00161 5.27378L14.4702 11.7446C14.6394 11.9121 14.8683 12.0055 15.1065 12.0043C15.3447 12.0031 15.5726 11.9074 15.7402 11.7382C15.9077 11.569 16.0012 11.3402 16 11.1022C15.9988 10.8641 15.903 10.6363 15.7338 10.4688L8.63468 3.37796C8.46644 3.21088 8.23878 3.11729 8.00161 3.1177Z" fill="#0070D1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 691 B |
|
|
@ -1,6 +1,6 @@
|
|||
import { shallowMount } from "@vue/test-utils";
|
||||
import inshopSchedulingCard from "./inshop-scheduling-card";
|
||||
import { RouteCodeFlags } from "@/constants/schedule-constants";
|
||||
import { RouteCodeFlags, AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||
|
||||
const ALL_DAY_DROPOFF_ROUTE_CODE = `03357-001820-I*21346*${RouteCodeFlags.ALL_DAY_DROP_OFF}`;
|
||||
|
|
@ -142,10 +142,9 @@ describe("inshop-scheduling-card.vue", () => {
|
|||
await dropOffInput.setValue(true);
|
||||
await wrapper.setProps({
|
||||
modelValue: {
|
||||
selectedInshopTimeSlot: {
|
||||
providerNumber: "001820",
|
||||
routeCodeId: ALL_DAY_DROPOFF_ROUTE_CODE,
|
||||
},
|
||||
appointmentType: AppointmentTypeStrings.IN_SHOP,
|
||||
providerNumber: "001820",
|
||||
routeCodeId: ALL_DAY_DROPOFF_ROUTE_CODE,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -158,10 +157,9 @@ describe("inshop-scheduling-card.vue", () => {
|
|||
const dropOffInput = wrapper.find(`input[value="${ALL_DAY_DROPOFF_ROUTE_CODE}"]`);
|
||||
await dropOffInput.setValue(true);
|
||||
expect(wrapper.emitted("update:modelValue")[0][0]).toEqual({
|
||||
selectedInshopTimeSlot: {
|
||||
providerNumber: "001820",
|
||||
routeCodeId: ALL_DAY_DROPOFF_ROUTE_CODE,
|
||||
},
|
||||
appointmentType: AppointmentTypeStrings.IN_SHOP,
|
||||
providerNumber: "001820",
|
||||
routeCodeId: ALL_DAY_DROPOFF_ROUTE_CODE,
|
||||
});
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
|
@ -202,10 +200,9 @@ describe("inshop-scheduling-card.vue", () => {
|
|||
const morningInput = wrapper.find(`input[value="${MORNING_INSHOP_ROUTE_CODE}"]`);
|
||||
await morningInput.setValue(true);
|
||||
expect(wrapper.emitted("update:modelValue")[0][0]).toEqual({
|
||||
selectedInshopTimeSlot: {
|
||||
providerNumber: "001820",
|
||||
routeCodeId: MORNING_INSHOP_ROUTE_CODE,
|
||||
},
|
||||
appointmentType: AppointmentTypeStrings.IN_SHOP,
|
||||
providerNumber: "001820",
|
||||
routeCodeId: MORNING_INSHOP_ROUTE_CODE,
|
||||
});
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
|
@ -213,10 +210,9 @@ describe("inshop-scheduling-card.vue", () => {
|
|||
test("highlights selected time slot", async () => {
|
||||
const wrapper = mountComponent({
|
||||
modelValue: {
|
||||
selectedInshopTimeSlot: {
|
||||
providerNumber: "001820",
|
||||
routeCodeId: MORNING_INSHOP_ROUTE_CODE,
|
||||
},
|
||||
appointmentType: AppointmentTypeStrings.IN_SHOP,
|
||||
providerNumber: "001820",
|
||||
routeCodeId: MORNING_INSHOP_ROUTE_CODE,
|
||||
},
|
||||
});
|
||||
const selectedSlot = wrapper.find(".inshop-scheduling-card__slot--selected");
|
||||
|
|
@ -225,6 +221,18 @@ describe("inshop-scheduling-card.vue", () => {
|
|||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("does not highlight selection from another provider", () => {
|
||||
const wrapper = mountComponent({
|
||||
modelValue: {
|
||||
appointmentType: AppointmentTypeStrings.IN_SHOP,
|
||||
providerNumber: "999999",
|
||||
routeCodeId: MORNING_INSHOP_ROUTE_CODE,
|
||||
},
|
||||
});
|
||||
expect(wrapper.find(".inshop-scheduling-card__slot--selected").exists()).toBe(false);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("collapses and expands body when header is clicked", async () => {
|
||||
const wrapper = mountComponent();
|
||||
expect(wrapper.find(".inshop-scheduling-card__body").isVisible()).toBe(false);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
<img
|
||||
class="inshop-scheduling-card__chevron"
|
||||
:class="{ 'inshop-scheduling-card__chevron--expanded': isExpanded }"
|
||||
src="@/assets/img/icons/chevron-right-blue.svg"
|
||||
src="@/assets/img/icons/chevron-no-background.svg"
|
||||
alt=""
|
||||
aria-hidden="true" />
|
||||
</button>
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { RouteCodeFlags } from "@/constants/schedule-constants";
|
||||
import { RouteCodeFlags, AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||
import {
|
||||
groupInshopTimeSlotsByTimeOfDay,
|
||||
INSHOP_INITIAL_VISIBLE_TIME_SLOTS,
|
||||
|
|
@ -145,7 +145,7 @@ export default {
|
|||
},
|
||||
radioGroupName: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: "schedulingTimeSlot",
|
||||
},
|
||||
cmsWidgetName: {
|
||||
type: String,
|
||||
|
|
@ -182,7 +182,7 @@ export default {
|
|||
if (miles == null) {
|
||||
return "";
|
||||
}
|
||||
const rounded = Math.round(miles * 2) / 2;
|
||||
const rounded = Math.round(miles * 10) / 10;
|
||||
return `${rounded} mi`;
|
||||
},
|
||||
formattedAddress() {
|
||||
|
|
@ -265,7 +265,13 @@ export default {
|
|||
return `${this.appointmentCount} appt${this.appointmentCount === 1 ? "" : "s"}`;
|
||||
},
|
||||
selectedRouteCodeValue() {
|
||||
return this.modelValue?.selectedInshopTimeSlot?.routeCodeId ?? null;
|
||||
if (
|
||||
this.modelValue?.appointmentType !== AppointmentTypeStrings.IN_SHOP ||
|
||||
this.modelValue?.providerNumber !== this.provider.providerNumber
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return this.modelValue?.routeCodeId ?? null;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -305,10 +311,9 @@ export default {
|
|||
},
|
||||
onTimeSlotSelected(slot) {
|
||||
this.$emit("update:modelValue", {
|
||||
selectedInshopTimeSlot: {
|
||||
providerNumber: this.provider.providerNumber,
|
||||
routeCodeId: slot.routeCodeId,
|
||||
},
|
||||
appointmentType: AppointmentTypeStrings.IN_SHOP,
|
||||
providerNumber: this.provider.providerNumber,
|
||||
routeCodeId: slot.routeCodeId,
|
||||
});
|
||||
},
|
||||
},
|
||||
|
|
@ -382,11 +387,11 @@ export default {
|
|||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
transform: rotate(90deg);
|
||||
transform: rotate(180deg);
|
||||
transition: transform 150ms linear;
|
||||
|
||||
&--expanded {
|
||||
transform: rotate(-90deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { shallowMount } from "@vue/test-utils";
|
||||
import mobileSchedulingCard from "./mobile-scheduling-card";
|
||||
import { PREMIUM_TIME_SLOT_ID_FLAG } from "@/constants/schedule-constants";
|
||||
import { PREMIUM_TIME_SLOT_ID_FLAG, AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||
|
||||
const MOCK_TIME_SLOTS = [
|
||||
|
|
@ -54,6 +54,7 @@ function mountComponent(props = {}, cmsContent = {}) {
|
|||
return shallowMount(mobileSchedulingCard, {
|
||||
props: {
|
||||
zipCode: "43235",
|
||||
providerNumber: "03357",
|
||||
timeSlots: MOCK_TIME_SLOTS,
|
||||
premiumTimeSlotPrice: 29.99,
|
||||
...props,
|
||||
|
|
@ -102,10 +103,10 @@ describe("mobile-scheduling-card.vue", () => {
|
|||
);
|
||||
await premiumInput.setValue(true);
|
||||
expect(wrapper.emitted("update:modelValue")[0][0]).toEqual({
|
||||
selectedMobileTimeSlot: {
|
||||
routeCodeId: MOCK_TIME_SLOTS[0].id,
|
||||
isPremiumAppointment: true,
|
||||
},
|
||||
appointmentType: AppointmentTypeStrings.MOBILE,
|
||||
providerNumber: "03357",
|
||||
routeCodeId: MOCK_TIME_SLOTS[0].id,
|
||||
isPremiumAppointment: true,
|
||||
});
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
|
@ -120,10 +121,10 @@ describe("mobile-scheduling-card.vue", () => {
|
|||
await premiumInput.setValue(true);
|
||||
await wrapper.setProps({
|
||||
modelValue: {
|
||||
selectedMobileTimeSlot: {
|
||||
routeCodeId: MOCK_TIME_SLOTS[0].id,
|
||||
isPremiumAppointment: true,
|
||||
},
|
||||
appointmentType: AppointmentTypeStrings.MOBILE,
|
||||
providerNumber: "03357",
|
||||
routeCodeId: MOCK_TIME_SLOTS[0].id,
|
||||
isPremiumAppointment: true,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -136,11 +137,23 @@ describe("mobile-scheduling-card.vue", () => {
|
|||
const pmInput = wrapper.find(`input[value="${MOCK_TIME_SLOTS[1].id}"]`);
|
||||
await pmInput.setValue(true);
|
||||
expect(wrapper.emitted("update:modelValue")[0][0]).toEqual({
|
||||
selectedMobileTimeSlot: {
|
||||
routeCodeId: MOCK_TIME_SLOTS[1].id,
|
||||
isPremiumAppointment: false,
|
||||
appointmentType: AppointmentTypeStrings.MOBILE,
|
||||
providerNumber: "03357",
|
||||
routeCodeId: MOCK_TIME_SLOTS[1].id,
|
||||
isPremiumAppointment: false,
|
||||
});
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("does not highlight selection from in-shop appointment", async () => {
|
||||
const wrapper = mountComponent({
|
||||
modelValue: {
|
||||
appointmentType: AppointmentTypeStrings.IN_SHOP,
|
||||
providerNumber: "001820",
|
||||
routeCodeId: MOCK_TIME_SLOTS[0].id,
|
||||
},
|
||||
});
|
||||
expect(wrapper.find(".mobile-scheduling-card__slot--selected").exists()).toBe(false);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
<img
|
||||
class="mobile-scheduling-card__chevron"
|
||||
:class="{ 'mobile-scheduling-card__chevron--expanded': isExpanded }"
|
||||
src="@/assets/img/icons/chevron-right-blue.svg"
|
||||
src="@/assets/img/icons/chevron-no-background.svg"
|
||||
alt=""
|
||||
aria-hidden="true" />
|
||||
</button>
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
<input
|
||||
type="radio"
|
||||
class="mobile-scheduling-card__slot-input"
|
||||
name="mobileSchedulingTimeSlot"
|
||||
:name="radioGroupName"
|
||||
:value="slot.value"
|
||||
:checked="isSlotSelected(slot)"
|
||||
@change="onTimeSlotSelected(slot)" />
|
||||
|
|
@ -84,7 +84,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { PREMIUM_TIME_SLOT_ID_FLAG } from "@/constants/schedule-constants";
|
||||
import { PREMIUM_TIME_SLOT_ID_FLAG, AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||
import { militaryToTwelveHourTime } from "@/layouts/schedule/helpers/schedule-helper";
|
||||
|
||||
export default {
|
||||
|
|
@ -107,6 +107,14 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
providerNumber: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
radioGroupName: {
|
||||
type: String,
|
||||
default: "schedulingTimeSlot",
|
||||
},
|
||||
showFreeFlag: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
|
|
@ -179,13 +187,18 @@ export default {
|
|||
return `+$${this.premiumTimeSlotPrice.toFixed(2)}`;
|
||||
},
|
||||
selectedRouteCodeValue() {
|
||||
const selected = this.modelValue?.selectedMobileTimeSlot;
|
||||
if (!selected?.routeCodeId) {
|
||||
if (
|
||||
this.modelValue?.appointmentType !== AppointmentTypeStrings.MOBILE ||
|
||||
this.modelValue?.providerNumber !== this.providerNumber
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return selected.isPremiumAppointment
|
||||
? this.addPremiumFlagToInput(selected.routeCodeId)
|
||||
: selected.routeCodeId;
|
||||
if (!this.modelValue?.routeCodeId) {
|
||||
return null;
|
||||
}
|
||||
return this.modelValue.isPremiumAppointment
|
||||
? this.addPremiumFlagToInput(this.modelValue.routeCodeId)
|
||||
: this.modelValue.routeCodeId;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -205,10 +218,10 @@ export default {
|
|||
},
|
||||
onTimeSlotSelected(slot) {
|
||||
this.$emit("update:modelValue", {
|
||||
selectedMobileTimeSlot: {
|
||||
routeCodeId: slot.routeCodeId,
|
||||
isPremiumAppointment: slot.isPremiumAppointment,
|
||||
},
|
||||
appointmentType: AppointmentTypeStrings.MOBILE,
|
||||
providerNumber: this.providerNumber,
|
||||
routeCodeId: slot.routeCodeId,
|
||||
isPremiumAppointment: slot.isPremiumAppointment,
|
||||
});
|
||||
},
|
||||
addPremiumFlagToInput(routeCode) {
|
||||
|
|
@ -300,11 +313,11 @@ export default {
|
|||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
transform: rotate(90deg);
|
||||
transform: rotate(180deg);
|
||||
transition: transform 150ms linear;
|
||||
|
||||
&--expanded {
|
||||
transform: rotate(-90deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,21 +22,23 @@
|
|||
<mobileSchedulingCard
|
||||
v-if="showMobileSchedulingCard"
|
||||
class="mt-4"
|
||||
v-model="selectedMobileScheduling"
|
||||
v-model="selectedScheduling"
|
||||
:providerNumber="mobileProviderAndTimeSlot.providerNumber"
|
||||
:timeSlots="mobileTimeSlotsForSelectedDate"
|
||||
:premiumTimeSlotPrice="premiumTimeSlotPrice"
|
||||
:zipCode="serviceZipCode"
|
||||
:showFreeFlag="showMobileFreeFlag"
|
||||
:radioGroupName="schedulingRadioGroupName"
|
||||
@zip-code-clicked="onMobileZipCodeClicked" />
|
||||
<inshopSchedulingCard
|
||||
v-for="{ provider } in inShopProvidersAndTimeslots"
|
||||
v-show="selectedDate"
|
||||
:key="provider.providerNumber"
|
||||
class="mt-4"
|
||||
v-model="selectedInshopSchedulingByProvider[provider.providerNumber]"
|
||||
v-model="selectedScheduling"
|
||||
:provider="provider"
|
||||
:timeSlots="getInshopTimeSlotsForSelectedDate(provider.providerNumber)"
|
||||
:radioGroupName="`inshopSchedulingTimeSlot-${provider.providerNumber}`"
|
||||
:radioGroupName="schedulingRadioGroupName"
|
||||
@address-clicked="onInshopAddressClicked(provider)" />
|
||||
<navbar
|
||||
cmsWidgetName="FunnelFooterWidget"
|
||||
|
|
@ -83,6 +85,7 @@ function toDateString(offsetDays, base = new Date()) {
|
|||
}
|
||||
|
||||
const SCHEDULE_FETCH_DAYS = 15;
|
||||
const SCHEDULING_RADIO_GROUP_NAME = "schedulingTimeSlot";
|
||||
|
||||
export default {
|
||||
name: "scheduling",
|
||||
|
|
@ -155,11 +158,13 @@ export default {
|
|||
},
|
||||
watch: {
|
||||
selectedDate() {
|
||||
this.selectedMobileScheduling = null;
|
||||
this.selectedInshopSchedulingByProvider = {};
|
||||
this.selectedScheduling = null;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
schedulingRadioGroupName() {
|
||||
return SCHEDULING_RADIO_GROUP_NAME;
|
||||
},
|
||||
serviceLocationText() {
|
||||
return this.getCmsContent("ServiceLocationText", "Text");
|
||||
},
|
||||
|
|
@ -208,8 +213,7 @@ export default {
|
|||
inShopProvidersAndTimeslots: [],
|
||||
mobileProviderAndTimeSlot: null,
|
||||
mobilePremiumAppointmentFee: null,
|
||||
selectedMobileScheduling: null,
|
||||
selectedInshopSchedulingByProvider: {},
|
||||
selectedScheduling: null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue