Merge branch 'develop' into feature/CSR-270

This commit is contained in:
Max 2022-02-22 10:38:25 -05:00
commit 86006d7600
15 changed files with 492 additions and 246 deletions

View file

@ -122,7 +122,7 @@ export default {
},
methods: {
handleCheckedChanged(val) {
if(this.isMultiSelect) {
if(this.isMultiSelect && this.selectedValues) {
// Add or remove item to array of data to emit
if(Array.isArray(this.selectedValues)) {
const newSelectedValues = this.selectedValues;

View file

@ -1,27 +1,75 @@
<template>
<h5>POC</h5>
<div class="container-fluid nested-radio">
<div class="row mb-1">
<div class="col">
<listCard
groupName="listcard1"
isWide="true"
buttonImage="https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3"
buttonLabel="Green Tint, Blue Shade"
buttonID="radio1"
/>
</div>
</div>
<div class="row mb-1">
<div class="col">
<p class="mb-0 fw-bold">Ok, select the features</p>
<buttonQuestion
buttonType="radio"
groupName="Demo Group"
buttonID="button1"
isRequired
value="test button"
screenReaderOnlyText="rain sensor, solar, 3rd visor band"
:answers="demoArray"
/>
</div>
</div>
<div class="row mb-2">
<div class="col">
<listCard
groupName="listcard1"
isWide="true"
buttonImage="https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3"
buttonLabel="Green Tint"
buttonID="radio2"
/>
</div>
</div>
</div>
</template>
<script>
import buttonQuestion from "@/common-components/button-question/button-question";
import listCard from "@/ux-components/list-card/list-card";
import radio from "@/ux-components/radio/radio";
export default {
name: "nestedRadio",
props: {
groupName: String,
buttonLabel: String,
buttonID: String,
isRequired: Boolean,
value: {
type: [String, Number],
default: "",
},
screenReaderOnlyText: String,
hasError: Boolean,
components: {
buttonQuestion,
listCard,
},
data() {
return {
demoArray: [
"rain sensor, solar, 3rd visor band",
"rain sensor, heated glass, solar, 3rd visor band",
]
}
}
};
</script>
<style lang="scss">
.nested-radio {
.ui-radio {
flex-direction: column;
margin: .25rem 0;
}
p {
font-size: .875rem;
}
}
</style>

View file

@ -6,6 +6,7 @@ import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
import sideDoorOptions from "@/layouts/vehicle-damage/side-door-options/side-door-options";
import damageLocationQuestion from "@/layouts/vehicle-damage/damage-location-question/damage-location-question";
import windshieldOptions from "@/layouts/vehicle-damage/windshield-options/windshield-options";
// Supporting Files
import { settleAllPromises } from "@/helpers/layout-helper.js";
@ -190,6 +191,9 @@ function setupMocks({
passengerSideOptions: {
availableReplacementOptions: ["Front", "Back", "Side"],
}
windshieldOptions: {
availableReplacementOptions: ["Single", "Driver", "Passenger"],
},
},
};
@ -219,6 +223,10 @@ function setupMocks({
initializeComponent: jest.fn(),
};
windshieldOptions.methods = {
initializeComponent: jest.fn(),
};
funnelFooter.methods = {
initializeComponent: jest.fn(),
}

View file

@ -5,6 +5,10 @@
<funnelSubHeader ref="funnelSubHeader" />
<damageLocationQuestion ref="damageLocation" v-model="selectedDamageLocations" groupName="DamageLocationQuestion" />
<sideDoorOptions ref="sideDoorOptions" isAvailable groupName="SideDoorSideQuestion" v-model="sideDoorOptionsData" />
<windshieldOptions ref="windshieldOptions"
v-model="selectedWindshieldOptions"
:selectedDamageLocations="selectedDamageLocations"
/>
<funnelFooter ref="funnelFooter" @back-clicked="backButtonAction" />
</div>
</template>
@ -17,6 +21,7 @@ import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
import sideDoorOptions from "@/layouts/vehicle-damage/side-door-options/side-door-options";
import damageLocationQuestion from "@/layouts/vehicle-damage/damage-location-question/damage-location-question";
import windshieldOptions from "@/layouts/vehicle-damage/windshield-options/windshield-options";
// Supporting files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
@ -68,6 +73,10 @@ export default {
vm.$refs.sideDoorOptions.initializeComponent(
resultMap.cmsContent.SideDoorSideQuestion, resultMap.cmsContent.DriverSideReplaceOptionsQuestion, resultMap.cmsContent.PassengerSideReplaceOptionsQuestion, resultMap.damageOptions.driverSideOptions.availableReplacementOptions, resultMap.damageOptions.passengerSideOptions.availableReplacementOptions
);
vm.$refs.windshieldOptions.initializeComponent(
resultMap.cmsContent.WindshieldDamageTypeQuestion, resultMap.cmsContent.WindshieldChipCountQuestion,
resultMap.cmsContent.WindshieldReplaceOptionsQuestion, resultMap.damageOptions.windshieldOptions.availableReplacementOptions
);
vm.$refs.funnelFooter.initializeComponent(
resultMap.cmsContent.FunnelFooterWidget
);
@ -81,7 +90,7 @@ export default {
selectedDriverSideReplacOptions: [],
selectedPassengerSideReplaceOptions: []
},
windshieldOptionsData: [],
selectedWindshieldOptions: []
}
},
methods: {
@ -107,6 +116,7 @@ export default {
funnelSubHeader,
sideDoorOptions,
damageLocationQuestion,
windshieldOptions,
},
};
</script>

View file

@ -1,99 +0,0 @@
<template>
<div class="windshield-damage-type-question">
<buttonQuestion
v-if="isAvailable && answersToDisplay.length > 1"
:questionText="questionText"
:answers="answersToDisplay"
:groupName="groupName"
buttonType="listCard"
v-model="selectedValues"
validationRules="windshield-damage-required|checkForRepairAndReplace:@DamageLocationQuestion"
:suppressError="suppressError"
/>
</div>
</template>
<script>
import buttonQuestion from "@/common-components/button-question/button-question";
import store from "@/store";
import { defineRule } from "vee-validate";
// DEFINE VALIDATION RULES
defineRule("windshield-damage-required", (value) => {
if (!value || value.length < 1) {
return "Please select windshield damage";
}
return true;
});
defineRule("checkForRepairAndReplace", (value, [other]) => {
if (value === "Windshield-Chip" && other.toString().includes("Windshield") && other.length > 1) {
return "checkForRepairAndReplace error"
}
return true;
});
export default ({
name: "windshieldDamageTypeQuestion",
data(){
return {
questionText: String,
answersFromCms: Array,
damageOptions: Object,
}
},
props: {
isMultiSelect: Boolean,
modelValue: Array,
isAvailable: Boolean,
name: String,
groupName: String,
suppressError: Boolean,
},
methods: {
initializeComponent(cmsContent, damageOptions){
this.questionText = cmsContent.QuestionText;
this.answersFromCms = cmsContent.Answers;
this.damageOptions = damageOptions;
},
},
computed: {
selectedValues: {
get: function() {
return this.modelValue;
},
set: function(newValue) {
this.$emit("update:modelValue", newValue);
}
},
damageOptionsMap(){
return {
Windshield: true,
SideDoor: this.damageOptions.driverSideOptions.availableReplacementOptions || this.damageOptions.passengerSideOptions.availableReplacementOption ? true : false,
RearWindow: this.damageOptions.backGlassOptions.availableReplacementOptions ? true : false,
}
},
answersToDisplay(){
// HARD CODED MOCK DATA FOR NOW
return [
{
"Name": "Windshield-Crack",
"Text": "Crack",
"SubText": "My damage is larger than six inches.",
"ImageId": "2cf13fbb-22d8-4b61-bd3d-3d413ea58c87",
"AnswerImageUrl": "https://fixmyglass.safelite.com/Shared/images/icon-replace-desktop-ds.png"
},
{
"Name": "Windshield-Chip",
"Text": "Chip(s)",
"SubText": "I have three or fewer chips smaller than six inches.",
"ImageId": "faaa14d9-3650-4949-a687-7665962337b8",
"AnswerImageUrl": "https://fixmyglass.safelite.com/Shared/images/icon-repair-desktop-ds.png"
}
]
},
},
components: {
buttonQuestion,
},
})
</script>

View file

@ -0,0 +1,71 @@
import { shallowMount } from "@vue/test-utils";
import windshieldChipCountQuestion from "@/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
import store from "@/store";
jest.mock("@/store", () => { return {}; }, {virtual: true});
describe("windshield-chip-count-question.vue", () => {
test("Selected chip count is emitted upon selection.", async () => {
//Arrange
const { wrapper } = setupMocks({modelValueProp: ["One"]});
//Act
wrapper.setValue({ modelValue: ["Two"] });
await wrapper.vm.$nextTick();
//Assert
expect(wrapper.vm.selectedChipCountValues).toEqual(["One"]);
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{ modelValue: ["Two"] }]);
});
});
describe("Windshield-chip-count-question.vue", () => {
test("Should display question and answers from api.", async () => {
//Arrange
const { wrapper, cmsContent } = setupMocks({modelValueProp: ["One"]});
//Act
windshieldChipCountQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent);
//Assert
expect(wrapper.vm.questionText).toStrictEqual("How many chips are we repairing?");
expect(wrapper.vm.answersFromCms).toStrictEqual([ { Name: 'One' }, { Name: 'Two' }, { Name: 'Three'} ]);
});
});
function setupMocks({
modelValueProp = ["Two"],
groupName = "WindshieldChipCountQuestion",
cmsQuestionText = "How many chips are we repairing?",
cmsAnswers = [{Name: "One"}, {Name: "Two"}, {Name: "Three"}],
dataFromStoreApi = [],
}) {
//Mock store
store.dispatch = jest.fn(() => dataFromStoreApi);
store.getters = { vehicle: {year: 2019, make: 'honda', model: 'civc', style: '2 Door', category: 'CAR'} };
const mountOptions = getMountOptions({
store: {
dispatch: store.dispatch,
getters: store.getters,
},
});
//Mock props
mountOptions.propsData = {
modelValue: modelValueProp
};
const wrapper = shallowMount(windshieldChipCountQuestion, mountOptions);
//Mock CMS content
const cmsContent = {
groupName: groupName,
QuestionText: cmsQuestionText,
Answers: cmsAnswers,
};
const damageOptions = dataFromStoreApi;
return { wrapper, cmsContent, damageOptions };
}

View file

@ -0,0 +1,52 @@
<template>
<transition name="fade">
<div class="windshield-chip-count-question">
<buttonQuestion
v-if="isAvailable"
:questionText="questionText"
:answers="answersFromCms"
:groupName="groupName"
buttonType="listButtonHorizontal"
v-model="selectedChipCountValues"
/>
</div>
</transition>
</template>
<script>
import buttonQuestion from "@/common-components/button-question/button-question";
export default ({
name: "windshieldOptions",
data(){
return {
questionText: String,
answersFromCms: Array
}
},
props: {
modelValue: Array,
groupName: String,
isAvailable: Boolean,
},
methods: {
initializeComponent(cmsContent){
this.questionText = cmsContent.QuestionText;
this.answersFromCms = cmsContent.Answers;
}
},
computed: {
selectedChipCountValues: {
get: function() {
return this.modelValue;
},
set: function(newValue) {
this.$emit("update:modelValue", newValue);
}
},
},
components: {
buttonQuestion,
}
})
</script>

View file

@ -0,0 +1,71 @@
import { shallowMount } from "@vue/test-utils";
import windshieldDamageTypeQuestion from"@/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
import store from "@/store";
jest.mock("@/store", () => { return {}; }, {virtual: true});
describe("windshield-damage-type-question.vue", () => {
test("Selected chip count is emitted upon selection.", async () => {
//Arrange
const { wrapper } = setupMocks({modelValueProp: ["Repair"]});
//Act
wrapper.setValue({ modelValue: ["Replace"] });
await wrapper.vm.$nextTick();
//Assert
expect(wrapper.vm.selectedValues).toEqual(["Repair"]);
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{ modelValue: ["Replace"] }]);
});
});
describe("windshield-damage-type-question.vue", () => {
test("Should display question and answers from api.", async () => {
//Arrange
const { wrapper, cmsContent } = setupMocks({modelValueProp: ["Repair"]});
//Act
windshieldDamageTypeQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent);
//Assert
expect(wrapper.vm.questionText).toStrictEqual("What's your windshield damage?");
expect(wrapper.vm.answersFromCms).toStrictEqual([ { Name: 'Repair' }, { Name: 'Replace' } ]);
});
});
function setupMocks({
modelValueProp = ["Two"],
groupName = "WindshieldDamageTypeQuestion",
cmsQuestionText = "What's your windshield damage?",
cmsAnswers = [{Name: "Repair"}, {Name: "Replace"}],
dataFromStoreApi = [],
}) {
//Mock store
store.dispatch = jest.fn(() => dataFromStoreApi);
store.getters = { vehicle: {year: 2019, make: 'honda', model: 'civc', style: '2 Door', category: 'CAR'} };
const mountOptions = getMountOptions({
store: {
dispatch: store.dispatch,
getters: store.getters,
},
});
//Mock props
mountOptions.propsData = {
modelValue: modelValueProp
};
const wrapper = shallowMount(windshieldDamageTypeQuestion, mountOptions);
//Mock CMS content
const cmsContent = {
groupName: groupName,
QuestionText: cmsQuestionText,
Answers: cmsAnswers,
};
const damageOptions = dataFromStoreApi;
return { wrapper, cmsContent, damageOptions };
}

View file

@ -0,0 +1,71 @@
<template>
<transition name="fade">
<div class="windshield-damage-type-question">
<buttonQuestion
v-if="isAvailable"
:questionText="questionText"
:answers="answersFromCms"
:groupName="groupName"
buttonType="listCard"
v-model="selectedValues"
/>
</div>
</transition>
</template>
<script>
import buttonQuestion from "@/common-components/button-question/button-question";
//import { defineRule } from "vee-validate";
//DEFINE VALIDATION RULES
// add these to the button component above:
// validationRules="windshield-damage-required|checkForRepairAndReplace:@DamageLocationQuestion"
// :suppressError="suppressError"
// defineRule("windshield-damage-required", (value) => {
// if (!value || value.length < 1) {
// return "Please select windshield damage";
// }
// return true;
// });
// defineRule("checkForRepairAndReplace", (value, [other]) => {
// if (value === "Windshield-Chip" && other.toString().includes("Windshield") && other.length > 1) {
// return "checkForRepairAndReplace error"
// }
// return true;
// });
export default ({
name: "windshieldDamageTypeQuestion",
data(){
return {
questionText: String,
answersFromCms: Array
}
},
props: {
modelValue: Array,
groupName: String,
isAvailable: Boolean,
suppressError: Boolean,
},
methods: {
initializeComponent(cmsContent){
this.questionText = cmsContent.QuestionText;
this.answersFromCms = cmsContent.Answers;
}
},
computed: {
selectedValues: {
get: function() {
return this.modelValue;
},
set: function(newValue) {
this.$emit("update:modelValue", newValue);
}
},
},
components: {
buttonQuestion,
}
})
</script>

View file

@ -1,26 +1,112 @@
<template>
<div class="windshield-options">
<windshieldDamageTypeQuestion
ref="windshieldDamageType"
v-model="selectedWindshieldDamageTypes"
groupName="windshieldDamageTypeQuestion"
:isAvailable="showWindshieldDamageTypeQuestion"
:suppressError="suppressError"
/>
</div>
<transition name="fade">
<div class="windshield-options" aria-live="polite">
<windshieldDamageTypeQuestion ref="windshieldDamageTypeQuestion"
:isAvailable=isWindshieldDamageLocation
:suppressError="suppressError"
groupName="WindshieldDamageTypeQuestion"
v-model="selectedWindshieldDamageTypeValues"
/>
<windshieldChipCountQuestion ref="windshieldChipCountQuestion"
:isAvailable=isRepairOptionSelected
groupName="WindshieldChipCountQuestion"
v-model="selectedWindshieldChipCountValues"
/>
<replaceOptionsQuestion ref="replaceOptionsQuestion"
:isAvailable=isReplaceOptionSelected
groupName="WindshieldReplaceOptions"
v-model="selectedWindshieldReplaceOptionsValues"
/>
</div>
</transition>
</template>
<script>
import windshieldDamageTypeQuestion from "@/layouts/vehicle-damage/windshield-damage-type-question/windshield-damage-type-question";
import windshieldDamageTypeQuestion from"@/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question";
import windshieldChipCountQuestion from"@/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question";
import replaceOptionsQuestion from"@/layouts/vehicle-damage/replace-options-question/replace-options-question";
export default ({
name: "windshieldOptions",
props: {
showWindshieldDamageTypeQuestion: Boolean,
suppressError: Boolean,
modelValue: Array,
selectedDamageLocations: Array,
selectedChipCount: Array,
selectedReplaceOptions: Array,
suppressError: Boolean
},
methods: {
initializeComponent(windshieldDamageTypeQuestionFromCms, windshieldChipCountQuestionFromCms,
windshieldReplaceOptionsQuestionFromCms, windshieldAvailableReplacementOptions){
this.$refs.windshieldDamageTypeQuestion.initializeComponent(windshieldDamageTypeQuestionFromCms);
this.$refs.windshieldChipCountQuestion.initializeComponent(windshieldChipCountQuestionFromCms);
this.$refs.replaceOptionsQuestion.initializeComponent(windshieldReplaceOptionsQuestionFromCms, windshieldAvailableReplacementOptions);
},
getWindshieldOptions(selectedWindshieldDamageType, selectedWindshieldChipCount, selectedWindshieldReplaceOptions){
return {
selectedWindshieldDamageType: selectedWindshieldDamageType,
selectedWindshieldChipCount: selectedWindshieldChipCount,
selectedWindshieldReplaceOptions: selectedWindshieldReplaceOptions
}
},
},
computed: {
selectedValues: {
get: function() {
return this.modelValue;
},
set: function(newValue) {
this.$emit("update:modelValue", newValue);
}
},
selectedWindshieldDamageTypeValues:{
get: function() {
return this.selectedValues.selectedWindshieldDamageType;
},
set: function(newValue) {
this.selectedValues = this.getWindshieldOptions(newValue, null, null);
}
},
selectedWindshieldChipCountValues: {
get: function() {
return this.selectedValues.selectedChipCount;
},
set: function(newValue) {
this.selectedValues = this.getWindshieldOptions(this.selectedWindshieldDamageTypeValues, newValue, null);
}
},
selectedWindshieldReplaceOptionsValues: {
get: function() {
return this.selectedValues.selectedWindshieldReplaceOptions;
},
set: function(newValue) {
this.selectedValues = this.getWindshieldOptions(this.selectedWindshieldDamageTypeValues, null, newValue);
}
},
isWindshieldDamageLocation() {
return this.selectedDamageLocations.some(selectedDamages =>
{
const categoryAndGlass = selectedDamages.split('-'); //ie: "Suv-Windshield" Splits the answers into [0]"Suv" [1]"Windshield"
return Boolean(categoryAndGlass[1].toUpperCase() === "WINDSHIELD");
});
},
isRepairOptionSelected(){
if (!this.selectedWindshieldDamageTypeValues) return false;
return this.selectedWindshieldDamageTypeValues.some(val => val.toUpperCase() === "REPAIR") && this.isWindshieldDamageLocation;
},
isReplaceOptionSelected(){
if (!this.selectedWindshieldDamageTypeValues) return false;
return this.selectedWindshieldDamageTypeValues.some(val => val.toUpperCase() === "REPLACE") && this.isWindshieldDamageLocation;
},
},
components: {
windshieldDamageTypeQuestion,
windshieldChipCountQuestion,
replaceOptionsQuestion
},
})
</script>

View file

@ -22,6 +22,8 @@
}
&.ui-radio,
&.ui-checkbox {
input[type=checkbox],
input[type=radio],
input[type=radio]+label:before,
input[type=checkbox]+label:before {
border: 1px solid $red;

View file

@ -1,15 +1,15 @@
<template>
<!-- Checkbox groups MUST be wrapped in a <fieldset> and <legend> tag -->
<div class="ui-checkbox d-flex" :class="[hasError ? 'has-error' : '']">
<input
type="checkbox"
aria-checked="false"
:name="checkboxName"
:id="buttonID"
:tabindex="tabIndex"
:aria-required="isRequired"
<div class="form-check ui-checkbox" :class="[hasError ? 'has-error' : '']">
<input class="form-check-input"
type="checkbox"
aria-checked="false"
:name="checkboxName"
:id="buttonID"
:tabindex="tabIndex"
:aria-required="isRequired"
/>
<label class="d-flex align-items-center" :for="buttonID">
<label class="d-flex align-items-start" :for="buttonID">
<p v-if="checkboxLabel" class="m-0">{{ checkboxLabel }}</p>
<span v-if="screenReaderOnlyText" class="sr-only">{{
screenReaderOnlyText
@ -33,61 +33,22 @@ export default {
};
</script>
<style lang="scss">
.ui-checkbox {
position: relative;
input[type="checkbox"] {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
+ label {
display: block;
position: relative;
&:hover {
cursor: pointer;
}
<style lang="scss" scoped>
.form-check {
.form-check-input {
border: 1px solid $gray-500;
border-radius: 2px;
&:checked {
background-size: 125%;
border: 1px solid $blue;
}
+ label::before {
content: "";
position: relative;
display: inline-block;
margin-right: 8px;
width: 16px;
height: 16px;
background: white;
border: 1px solid $gray-500;
border-radius: 2px;
}
&:checked + label::before {
background: $blue;
}
&:checked + label::after {
content: "";
position: absolute;
top: 8px;
left: 2px;
border-left: 2px solid $white;
border-bottom: 2px solid $white;
height: 6px;
width: 12px;
transform: rotate(-45deg);
}
&:hover + label::before {
&:focus {
box-shadow: 0 0 0 4px $blue-300;
}
&:focus + label::before {
box-shadow: 0 0 0 2px $blue;
}
&:focus:checked + label::before {
box-shadow: 0 0 0 2px transparent;
}
&:disabled + label {
color: $gray-200;
}
&:disabled + label::before {
background: $gray-200;
}
&:hover {
.form-check-input {
box-shadow: 0 0 0 4px $blue-300;
}
}
}

View file

@ -133,7 +133,7 @@ export default {
height: 0;
&:focus-visible + label {
box-shadow: 0 0 0 2px $blue;
z-index: 3;
z-index: 2;
}
&:focus + label {
box-shadow: 0 0 0 2px $blue;
@ -159,7 +159,7 @@ export default {
&:hover {
box-shadow: 0 0 0 4px $blue-300;
cursor: pointer;
z-index: 3 !important;
z-index: 4 !important;
}
+ p {
display: none;

View file

@ -173,6 +173,7 @@ export default {
&:checked + label {
p {
color: $black;
font-weight: 500;
}
.sub-copy {
color: $gray-600;

View file

@ -1,8 +1,9 @@
<template>
<!-- Checkbox groups MUST be wrapped in a <fieldset> and <legend> tag and must contain tabindex -->
<div class="ui-radio d-flex" :class="[hasError ? 'has-error' : '']">
<div class="ui-radio form-check" :class="[hasError ? 'has-error' : '']">
<input
type="radio"
class="form-check-input"
aria-checked="false"
:name="groupName"
:id="buttonID"
@ -11,7 +12,7 @@
:v-model="checkValue"
@change="handleCheckChanged()"
/>
<label class="d-flex align-items-center" :for="buttonID">
<label class="d-flex align-items-start form-check-label" :for="buttonID">
<p v-if="buttonLabel" class="m-0">{{ buttonLabel }}</p>
<span v-if="screenReaderOnlyText" class="sr-only">{{
screenReaderOnlyText
@ -77,62 +78,25 @@ export default {
};
</script>
<style lang="scss">
.ui-radio {
position: relative;
input[type="radio"] {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
+ label {
display: block;
position: relative;
&:hover {
cursor: pointer;
}
}
+ label::before {
content: "";
position: relative;
display: inline-block;
margin-right: 8px;
width: 16px;
height: 16px;
background: white;
border: 1px solid $gray-500;
border-radius: 50%;
}
&:checked + label::before {
background: $white;
<style lang="scss" scoped>
.form-check {
.form-check-input {
border: 1px solid $gray-500;
border-radius: 100%;
margin-right: .5rem;
&:checked {
background-color: $white;
background-size: 71%;
border: 1px solid $blue;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 50 50' xml:space='preserve'%3e%3ccircle cx='25' cy='25' r='25' fill='%231574A1'/%3e%3c/svg%3e");
}
&:checked + label::after {
content: "";
position: absolute;
top: 8px;
left: 3px;
height: 6px;
width: 6px;
transform: rotate(-45deg);
border: 5px solid $blue;
border-radius: 50%;
}
&:hover + label::before {
&:focus {
box-shadow: 0 0 0 4px $blue-300;
}
&:focus + label::before {
box-shadow: 0 0 0 2px $blue;
}
&:focus:checked + label::before {
box-shadow: 0 0 0 2px transparent;
}
&:disabled + label {
color: $gray-200;
}
&:disabled + label::before {
background: $gray-200;
}
&:hover {
.form-check-input {
box-shadow: 0 0 0 4px $blue-300;
}
}
}