CSR-186 strip logic from button and card components.
This commit is contained in:
parent
6bf01676da
commit
85a07de718
3 changed files with 22 additions and 335 deletions
|
|
@ -1,123 +1,17 @@
|
|||
<!-- See the component-test.vue page for example implementation -->
|
||||
<template>
|
||||
<!-- IMPORTANT: Refrain from using more than 4 horizontal buttons on desktop, 3 on mobile. -->
|
||||
<div
|
||||
v-if="isMultiSelect"
|
||||
class="list-group list-button-horizontal d-flex flex-column w-100 mb-2"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
:id="buttonID"
|
||||
:name="groupName"
|
||||
:value="buttonID"
|
||||
:aria-required="isRequired"
|
||||
@click="handleClick(value)"
|
||||
:data-focus-target="groupName"
|
||||
/>
|
||||
<label
|
||||
tabindex="-1"
|
||||
:for="buttonID"
|
||||
:aria-labelledby="buttonID"
|
||||
class="d-flex flex-column justify-content-center py-3 px-4"
|
||||
:class="isFirstOrLastButton"
|
||||
>
|
||||
<span class="m-0" :class="[this.textPosition]">{{buttonID}}</span>
|
||||
<span v-if="screenReaderOnlyText" class="sr-only">{{screenReaderOnlyText}}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="col list-group list-button-horizontal d-flex flex-column mb-2"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
:id="buttonID"
|
||||
:name="groupName"
|
||||
:value="buttonID"
|
||||
:aria-required="isRequired"
|
||||
@click="handleClick(value)"
|
||||
:data-focus-target="groupName"
|
||||
/>
|
||||
<label
|
||||
tabindex="-1"
|
||||
:for="buttonID"
|
||||
:aria-labelledby="buttonID"
|
||||
class="d-flex flex-column justify-content-center py-3 px-4"
|
||||
:class="isFirstOrLastButton"
|
||||
>
|
||||
<span class="m-0" :class="textPosition">{{buttonID}}</span>
|
||||
<span v-if="screenReaderOnlyText" class="sr-only">{{screenReaderOnlyText}}</span>
|
||||
<loader v-if="isLoaderDisplayed" :style="{width: `${sizeInRem}rem`, height: `${sizeInRem}rem`}" :class="[loaderColor, loaderPosition]" />
|
||||
<div class="list-group list-button-horizontal d-flex flex-column w-100 mb-2">
|
||||
<input type="checkbox" id="buttonID" name="groupName" value="buttonID" aria-required="isRequired"/>
|
||||
<label tabindex="1" for="buttonID" aria-labelledby="buttonID" class="d-flex flex-column justify-content-center py-3 px-4">
|
||||
<span class="m-0">Button ID</span>
|
||||
<span class="sr-only">Screenreader Text</span>
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { toRefs } from 'vue';
|
||||
import { useField } from 'vee-validate';
|
||||
import loader from "@/ux-components/loader/loader";
|
||||
|
||||
export default {
|
||||
name: "listButtonHorizontal",
|
||||
props: {
|
||||
isMultiSelect: Boolean, /* Defines use as checkbox */
|
||||
groupName: String, /* Required, unique for each button GROUP */
|
||||
buttonID: String, /* Required, unique for each button. Used for button id, label and <label for> */
|
||||
screenReaderOnlyText: String, /* Optional, copy to be read by screenreader */
|
||||
textPosition: String, /* Optional, use Bootstrap classes: text-start, text-center, text-end. Default (empty) is text-start */
|
||||
loaderEnabled: Boolean, /* Optional, need to include this for loader to be used at all */
|
||||
loaderColor: String, /* Specify color of loader/spinner. Options are blue, red, green, white, black. Default is blue */
|
||||
loaderPosition: String, /* Specify horizontal position of loader/spinner. Options are center, right, left */
|
||||
sizeInRem: Number, /* Specify size of loader/spinner in rem. Example: 1.5 (equals 24px (16x1.5)) */
|
||||
totalInGroup: Number, /* Required, total number of buttons in group. Used to tell first and last in group to apply border radius. */
|
||||
positionInGroup: Number, /* Required, position of button in group. Example, 1,2,3 */
|
||||
isRequired: Boolean,
|
||||
value: { // Field initial value
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoaderDisplayed: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
displayLoader() {
|
||||
this.isLoaderDisplayed = true;
|
||||
},
|
||||
handleClick(value) {
|
||||
this.loaderEnabled && this.displayLoader();
|
||||
this.handleChange(value);
|
||||
}
|
||||
},
|
||||
components: {
|
||||
loader,
|
||||
},
|
||||
computed: {
|
||||
isFirstOrLastButton() {
|
||||
let className = "";
|
||||
if (this.positionInGroup == this.totalInGroup) {
|
||||
className = "last-item";
|
||||
} else if (this.positionInGroup == 1) {
|
||||
className = "first-item";
|
||||
}
|
||||
return className;
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { groupName, value } = toRefs(props);
|
||||
const inputType = props.isMultiSelect ? "checkbox" : "radio";
|
||||
const { checked, handleChange, errorMessage } = useField(groupName, undefined, {
|
||||
type: inputType,
|
||||
checkedValue: value
|
||||
});
|
||||
return {
|
||||
checked,
|
||||
handleChange,
|
||||
errorMessage,
|
||||
};
|
||||
},
|
||||
name: "listButtonHorizontal"
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,103 +1,17 @@
|
|||
<template>
|
||||
<!-- See the component-test.vue page for example implementation -->
|
||||
<div v-if="isMultiSelect" class="list-group list-button d-flex flex-column w-100 mb-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
:id="buttonID"
|
||||
:name="groupName"
|
||||
:value="buttonID"
|
||||
:aria-required="isRequired"
|
||||
@click="handleClick(value)"
|
||||
:data-focus-target="groupName"
|
||||
>
|
||||
<label
|
||||
tabindex="-1"
|
||||
:for="buttonID"
|
||||
:aria-labelledby="buttonID"
|
||||
class="d-flex flex-column justify-content-center py-3 px-4"
|
||||
>
|
||||
<span class="m-0" :class="[this.textPosition]">{{buttonID}}</span>
|
||||
<span v-if="buttonLabelSubCopy" class="m-0 small" :class="[this.textPosition]">{{buttonLabelSubCopy}}</span>
|
||||
<span v-if="screenReaderOnlyText" class="sr-only">{{screenReaderOnlyText}}</span>
|
||||
</label>
|
||||
<div class="list-group list-button d-flex flex-column w-100 mb-2">
|
||||
<input type="checkbox" id="buttonID" name="groupName" value="buttonID" aria-required="isRequired">
|
||||
<label tabindex="1" for="buttonID" aria-labelledby="buttonID" class="d-flex flex-column justify-content-center py-3 px-4">
|
||||
<span class="m-0">Button ID</span>
|
||||
<span class="m-0 small">Button Label Subcopy</span>
|
||||
<span class="sr-only">Screenreader Text</span>
|
||||
</label>
|
||||
</div>
|
||||
<div v-else class="list-group list-button d-flex flex-column w-100 mb-2">
|
||||
<input
|
||||
type="radio"
|
||||
:id="buttonID"
|
||||
:name="groupName"
|
||||
:value="buttonID"
|
||||
:aria-required="isRequired"
|
||||
@click="handleClick(value)"
|
||||
:data-focus-target="groupName"
|
||||
>
|
||||
<label
|
||||
tabindex="-1"
|
||||
:for="buttonID"
|
||||
:aria-labelledby="buttonID"
|
||||
class="d-flex flex-column justify-content-center py-3 px-4"
|
||||
>
|
||||
<span class="m-0" :class="[this.textPosition]">{{buttonID}}</span>
|
||||
<span v-if="buttonLabelSubCopy" class="m-0 small" :class="[this.textPosition]">{{buttonLabelSubCopy}}</span>
|
||||
<span v-if="screenReaderOnlyText" class="sr-only">{{screenReaderOnlyText}}</span>
|
||||
<loader v-if="isLoaderDisplayed" :style="{width: `${sizeInRem}rem`, height: `${sizeInRem}rem`}" :class="[this.loaderColor, this.loaderPosition]" />
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { toRefs } from 'vue';
|
||||
import { useField } from 'vee-validate';
|
||||
import loader from "@/ux-components/loader/loader";
|
||||
export default {
|
||||
name: "listButton",
|
||||
props: {
|
||||
isMultiSelect: Boolean, /* Defines use as checkbox */
|
||||
groupName: String, /* Required, unique for each button GROUP */
|
||||
buttonID: [Number,String], /* Required, unique for each button. Used for button id, label and <label for> */
|
||||
isRequired: Boolean, /* Optional, default is false */
|
||||
screenReaderOnlyText: String, /* Optional, copy to be read by screenreader */
|
||||
buttonLabelSubCopy: String, /* Optional, used for multi-line buttons */
|
||||
textPosition: String, /* Optional, use Bootstrap classes: text-start, text-center, text-end. Default (empty) is text-start */
|
||||
loaderEnabled: Boolean, /* Optional, need to include this for loader to be used at all */
|
||||
loaderColor: String, /* Specify color of loader/spinner. Options are blue, red, green, white, black. Default is blue */
|
||||
loaderPosition: String, /* Specify horizontal position of loader/spinner. Options are center, right, left */
|
||||
sizeInRem: [Number,String], /* Specify size of loader/spinner in rem. Example: 1.5 (equals 24px (16x1.5)) */
|
||||
value: { // Field initial value
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoaderDisplayed: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
displayLoader() {
|
||||
this.isLoaderDisplayed = true;
|
||||
},
|
||||
handleClick(value) {
|
||||
this.loaderEnabled && this.displayLoader();
|
||||
this.handleChange(value);
|
||||
}
|
||||
},
|
||||
components: {
|
||||
loader,
|
||||
},
|
||||
setup(props) {
|
||||
const { groupName, value } = toRefs(props);
|
||||
const inputType = props.isMultiSelect ? "checkbox" : "radio";
|
||||
const { checked, handleChange, errorMessage } = useField(groupName, undefined, {
|
||||
type: inputType,
|
||||
checkedValue: value
|
||||
});
|
||||
return {
|
||||
checked,
|
||||
handleChange,
|
||||
errorMessage,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
@ -111,4 +25,4 @@ export default {
|
|||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,143 +1,22 @@
|
|||
<template>
|
||||
<!-- Heavily documented below -->
|
||||
<div v-if="isMultiSelect" class="list-card w-100 rounded-3 d-flex align-items-center h-100">
|
||||
<input
|
||||
type="checkbox"
|
||||
:id="buttonID"
|
||||
:name="groupName"
|
||||
:value="buttonLabel"
|
||||
:aria-required="isRequired"
|
||||
@click="handleChange(value)"
|
||||
:data-focus-target="groupName"
|
||||
/>
|
||||
<label
|
||||
:for="buttonID"
|
||||
class="d-flex flex-column w-100 align-items-center pt-4 pb-2 px-2 h-100"
|
||||
tabindex="-1"
|
||||
>
|
||||
<img class="order-1" v-bind:src="require(`@/assets/img/icons/${buttonImage}`)" v-bind:alt="altText" />
|
||||
<p class="small m-0 order-3">{{buttonLabel}}</p>
|
||||
<p v-if="buttonLabelSubCopy" class="fs-7 m-0 order-4">{{buttonLabelSubCopy}}</p>
|
||||
</label>
|
||||
</div>
|
||||
<div v-else-if="isRadio" class="list-card w-100 rounded-3 d-flex align-items-center h-100">
|
||||
<input
|
||||
type="radio"
|
||||
:id="buttonID"
|
||||
:name="groupName"
|
||||
:value="buttonID"
|
||||
:aria-required="isRequired"
|
||||
@click="handleChange(value)"
|
||||
:data-focus-target="groupName"
|
||||
/>
|
||||
<label
|
||||
:for="buttonID"
|
||||
class="d-flex flex-column w-100 align-items-center pt-4 pb-2 px-2 h-100"
|
||||
tabindex="-1"
|
||||
>
|
||||
<img class="order-1" v-bind:src="require(`@/assets/img/icons/${buttonImage}`)" v-bind:alt="altText" />
|
||||
<p class="small mt-2 mb-0 order-2">{{buttonLabel}}</p>
|
||||
<p v-if="buttonLabelSubCopy" class="fs-7 m-0 order-3">{{buttonLabelSubCopy}}</p>
|
||||
</label>
|
||||
</div>
|
||||
<div v-else-if="isMultiSelectHorizontal" class="list-card horizontal w-100 rounded-3 d-flex align-items-center h-100">
|
||||
<input
|
||||
type="checkbox"
|
||||
:id="buttonID"
|
||||
:name="groupName"
|
||||
:value="buttonLabel"
|
||||
:aria-required="isRequired"
|
||||
@click="handleChange(value)"
|
||||
:data-focus-target="groupName"
|
||||
/>
|
||||
<label
|
||||
:for="buttonID"
|
||||
class="d-flex flex-row w-100 align-items-center py-r ps-3 pe-8 px-2 h-100"
|
||||
tabindex="-1"
|
||||
:class="{'checkboxTop': buttonLabelSubCopy}"
|
||||
>
|
||||
<img class="ms-auto order-3" v-bind:src="require(`@/assets/img/icons/${buttonImage}`)" v-bind:alt="altText" />
|
||||
<div class="order-2">
|
||||
<p class="m-0 small">{{buttonLabel}}</p>
|
||||
<p v-if="buttonLabelSubCopy" class="m-0 fs-7">{{buttonLabelSubCopy}}</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div v-else-if="isRadioHorizontal" class="list-card horizontal w-100 rounded-3 d-flex align-items-center h-100">
|
||||
<input
|
||||
type="radio"
|
||||
:id="buttonID"
|
||||
:name="groupName"
|
||||
:value="buttonLabel"
|
||||
:aria-required="isRequired"
|
||||
@click="handleChange(value)"
|
||||
:data-focus-target="groupName"
|
||||
/>
|
||||
<label
|
||||
:for="buttonID"
|
||||
class="d-flex flex-row w-100 align-items-center py-r ps-3 pe-8 px-2 h-100"
|
||||
tabindex="-1"
|
||||
:class="{'checkboxTop': buttonLabelSubCopy}"
|
||||
>
|
||||
<img class="ms-auto order-3" v-bind:src="require(`@/assets/img/icons/${buttonImage}`)" v-bind:alt="altText" />
|
||||
<div class="order-2">
|
||||
<p class="m-0 small">{{buttonLabel}}</p>
|
||||
<p v-if="buttonLabelSubCopy" class="m-0 fs-7">{{buttonLabelSubCopy}}</p>
|
||||
</div>
|
||||
<div class="list-card w-100 rounded-3 d-flex align-items-center h-100">
|
||||
<input type="checkbox" id="buttonID" name="groupName" value="buttonLabel" aria-required="isRequired"/>
|
||||
<label for="buttonID" class="d-flex flex-column w-100 align-items-center pt-4 pb-2 px-2 h-100" tabindex="1">
|
||||
<img class="order-1" v-bind:src="require(`@/assets/img/icons/${buttonImage}`)" alt="altText" />
|
||||
<p class="small m-0 order-3">Button Label</p>
|
||||
<p class="fs-7 m-0 order-4">Button Label Subcopy</p>
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { toRefs } from 'vue';
|
||||
import { useField } from 'vee-validate';
|
||||
|
||||
export default {
|
||||
name: "listCard",
|
||||
props: {
|
||||
//Must choose one of the following four options
|
||||
isMultiSelect: Boolean, //Defines use as checkbox
|
||||
isRadio: Boolean, //Defines use as radio button
|
||||
isMultiSelectHorizontal: Boolean, //Defines use as horizontal checkbox
|
||||
isRadioHorizontal: Boolean, //Defines use as horizontal radio button
|
||||
//end must choose
|
||||
buttonImage: String,//Required: File name of image
|
||||
buttonLabel: String,//Required: Label text
|
||||
isRequired: Boolean, //Required: is aria-required required or not?
|
||||
altText: String,//Leave empty. Screen readers read the buttonLabel text. If alt has content, it will repeat unnecessarily.
|
||||
buttonID: String,//Required: Unique
|
||||
groupName: String,//Rquired: Unique
|
||||
buttonLabelSubCopy: String,//Optional: sub text
|
||||
value: { // Field initial value
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const { groupName, value } = toRefs(props);
|
||||
const inputType = () => { // TODO: refactor props to streamline this logic for the whole component
|
||||
if (props.isMultiSelect) {
|
||||
return "checkbox";
|
||||
} else if (props.isRadio) {
|
||||
return "radio";
|
||||
} else if (props.isMultiSelectHorizontal) {
|
||||
return "checkbox";
|
||||
} else if (props.isRadioHorizontal) {
|
||||
return "radio";
|
||||
}
|
||||
}
|
||||
|
||||
props.isMultiSelect ? "checkbox" : "radio";
|
||||
const { checked, handleChange } = useField(groupName, undefined, {
|
||||
type: inputType(),
|
||||
checkedValue: value,
|
||||
});
|
||||
|
||||
return {
|
||||
checked,
|
||||
handleChange,
|
||||
};
|
||||
},
|
||||
buttonImage: String
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue