Initial work on moving heritage button styles to NextGen button component

Added button variant support
Added button size support
Merged button features from modal button to button component
Removed modal button component
Created button mixins for creating buttons
Created initial button variants using mixins
Moved client button customizations to mixins
This commit is contained in:
Josh Dassinger 2026-01-05 10:42:15 -06:00
parent e679c02582
commit fa965468b4
16 changed files with 342 additions and 555 deletions

View file

@ -0,0 +1,8 @@
const buttonSizes = Object.freeze({
xs: 'xs',
sm: 'sm',
md: 'md',
lg: 'lg'
});
export default buttonSizes;

View file

@ -0,0 +1,9 @@
const buttonVariants = Object.freeze({
primary: 'primary',
secondary: 'secondary',
success: 'success',
link: 'link',
navigation: 'navigation'
});
export default buttonVariants;

View file

@ -34,30 +34,18 @@
<slot></slot> <slot></slot>
</div> </div>
<div class="modal-footer px-5 py-4"> <div class="modal-footer px-5 py-4">
<modalButtonMain <buttonMain
v-if="useDefaultButton"
id="modalbtn" id="modalbtn"
ref="modalButtonMain" ref="modalButtonMain"
isPrimary :variant="buttonVariant"
class="w-100" class="w-100"
:buttonText="footerButtonText" :buttonText="footerButtonText"
pushToGA
:class="[ :class="[
(isButtonDisabled || isFooterButtonDisabled) && (isButtonDisabled || isFooterButtonDisabled) &&
'form-test-invalid', 'form-test-invalid'
additionalClassesString
]" ]"
@clickEvent="validateAndEmit" /> @clickEvent="validateAndEmit" />
<button
v-if="!useDefaultButton"
class="w-100 btn d-flex align-items-center justify-content-center delay"
:class="[
(isButtonDisabled || isFooterButtonDisabled) &&
'form-test-invalid',
additionalClassesString
]"
@click="validateAndEmit">
<span class="m-0">{{ footerButtonText }}</span>
</button>
</div> </div>
</div> </div>
</div> </div>
@ -65,7 +53,7 @@
</template> </template>
<script> <script>
import modalButtonMain from '@/ux-components/modal-button-main/modal-button-main.vue'; import ButtonMain from '@/ux-components/button-main/button-main.vue';
import { Modal } from 'bootstrap'; import { Modal } from 'bootstrap';
import { useForm } from 'vee-validate'; import { useForm } from 'vee-validate';
@ -73,7 +61,7 @@ export default {
// eslint-disable-next-line vue/multi-word-component-names // eslint-disable-next-line vue/multi-word-component-names
name: 'modal', name: 'modal',
components: { components: {
modalButtonMain ButtonMain
}, },
props: { props: {
modalId: String, modalId: String,
@ -86,13 +74,9 @@ export default {
type: Function type: Function
}, },
isButtonDisabled: Boolean, isButtonDisabled: Boolean,
useDefaultButton: { buttonVariant: {
type: Boolean,
default: true
},
additionalClassesString: {
type: String, type: String,
default: null default: 'success'
} }
}, },
emits: ['footer-button-event', 'isModalOpened'], emits: ['footer-button-event', 'isModalOpened'],

View file

@ -34,8 +34,7 @@
<buttonMain <buttonMain
ref="buttonMain" ref="buttonMain"
class="w-100" class="w-100"
isPrimary variant="success"
suppressLoader
:buttonText="ModalSelectButtonText" :buttonText="ModalSelectButtonText"
data-bs-dismiss="modal" data-bs-dismiss="modal"
@clickEvent="buttonClick" /> @clickEvent="buttonClick" />

View file

@ -7,19 +7,19 @@
ref="continueModal" ref="continueModal"
:headerText="modalHeaderText" :headerText="modalHeaderText"
:footerButtonText="modalFooterText" :footerButtonText="modalFooterText"
buttonVariant="primary"
@isModalOpened="setModalStatus" @isModalOpened="setModalStatus"
@footerButtonEvent="startNewReferral"> @footerButtonEvent="startNewReferral">
<p class="mb-2 subheader-text text-center"> <p class="mb-2 subheader-text text-center">
{{ modalSubHeaderText }} {{ modalSubHeaderText }}
</p> </p>
<button <buttonMain
id="btnContinueReferral" id="btnContinueReferral"
type="button" variant="success"
class="btn btn-white-blue continue-referral class="mt-2 w-100"
align-items-center justify-content-center d-inline-flex mt-2 py-3 px-4 delay w-100" :buttonText="modalBodyText"
@click="continueReferral"> @click="continueReferral">
{{ modalBodyText }} </buttonMain>
</button>
</modal> </modal>
</div> </div>
</transition> </transition>
@ -28,10 +28,12 @@
<script> <script>
import modal from '@/digital-components/modal/modal.vue'; import modal from '@/digital-components/modal/modal.vue';
import { getISSCookie } from '@/helpers/cookie-helper.js'; import { getISSCookie } from '@/helpers/cookie-helper.js';
import ButtonMain from '@/ux-components/button-main/button-main.vue';
export default { export default {
name: 'continue-modal', name: 'continue-modal',
components: { components: {
ButtonMain,
modal modal
}, },
emits: ['continue-previous-referral', 'start-new-referral'], emits: ['continue-previous-referral', 'start-new-referral'],
@ -80,16 +82,5 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.btn-white-blue {
border-radius: 0.5rem;
border-color: $blue;
color: $blue;
font-size: 1rem;
font-weight: $font-weight-bold;
line-height: 1.5rem;
background-color: #ffffff;
--bs-btn-focus-shadow-rgb: 218, 72, 62;
--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
</style> </style>

View file

@ -16,7 +16,7 @@
<buttonMain <buttonMain
v-if="!isForwardButtonHidden" v-if="!isForwardButtonHidden"
ref="buttonMain" ref="buttonMain"
isPrimary variant="navigation"
:buttonText="buttonText" :buttonText="buttonText"
:class=" :class="
(disableForwardAction || isForwardActionDisabled) && (disableForwardAction || isForwardActionDisabled) &&
@ -136,7 +136,7 @@ export default {
width: auto; width: auto;
justify-content: flex-end; justify-content: flex-end;
} }
.btn-primary { .btn-navigation {
width: auto; width: auto;
} }
a { a {
@ -149,7 +149,7 @@ export default {
overflow-x: visible; // needed to fix hidden footer on some iphones overflow-x: visible; // needed to fix hidden footer on some iphones
} }
div.flex-column { div.flex-column {
.btn-primary { .btn-navigation {
width: 100%; width: 100%;
justify-content: center; justify-content: center;
} }

View file

@ -8,20 +8,17 @@
ref="cancelClaimModal" ref="cancelClaimModal"
:headerText="modalHeaderText" :headerText="modalHeaderText"
:footerButtonText="modalFooterText" :footerButtonText="modalFooterText"
additionalClassesString="navigation-link" buttonVariant="link"
:useDefaultButton="false"
@isModalOpened="setModalStatus" @isModalOpened="setModalStatus"
@footerButtonEvent="cancelClaimConfirmation"> @footerButtonEvent="cancelClaimConfirmation">
<div v-html="modalBodyText"></div> <div v-html="modalBodyText"></div>
<button <buttonMain
id="btnContinueReferral" id="btnContinueReferral"
type="button" variant="success"
:aria-disabled="false" :buttonText="modalBodyText2"
class="btn btn-primary btn-override class="mt-4 w-100"
align-items-center justify-content-center d-inline-flex mt-4 py-3 px-4 delay w-100" @clickEvent="returnToClaim">
@click="returnToClaim"> </buttonMain>
{{ modalBodyText2 }}
</button>
</modal> </modal>
</div> </div>
</transition> </transition>
@ -29,10 +26,12 @@
<script> <script>
import modal from '@/digital-components/modal/modal.vue'; import modal from '@/digital-components/modal/modal.vue';
import ButtonMain from '@/ux-components/button-main/button-main.vue';
export default { export default {
name: 'cancel-claim-modal', name: 'cancel-claim-modal',
components: { components: {
ButtonMain,
modal modal
}, },
emits: ['cancel-claim-confirmation', 'return-to-claim'], emits: ['cancel-claim-confirmation', 'return-to-claim'],
@ -76,19 +75,5 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.btn {
&.btn-primary {
position: relative;
background: linear-gradient(270deg, $blue 0%, $blue-800 100%);
border: none;
border-radius: $border-radius-lg;
color: $white;
justify-content: center;
font-weight: $font-weight-bold;
letter-spacing: inherit;
@media (hover: hover) {
background: linear-gradient(270deg, $blue 0%, $blue-800 100%);
}
}
}
</style> </style>

View file

@ -169,7 +169,7 @@ describe('tpa-submit', () => {
// Assert // Assert
expect(mainButton.exists()).toBeTruthy(); expect(mainButton.exists()).toBeTruthy();
expect(mainButton.props().isPrimary).toBe(true); expect(mainButton.props().variant).toBe('success');
expect(mainButton.classes()).toContain('w-100'); expect(mainButton.classes()).toContain('w-100');
expect(mainButton.classes()).toContain('mb-5'); expect(mainButton.classes()).toContain('mb-5');
}); });

View file

@ -32,7 +32,7 @@
class="mb-4 small text-color--darker-gray" /> class="mb-4 small text-color--darker-gray" />
<buttonMain <buttonMain
ref="buttonMainOne" ref="buttonMainOne"
isPrimary variant="success"
:buttonText="forwardButtonText" :buttonText="forwardButtonText"
class="w-100 mb-5" class="w-100 mb-5"
@clickEvent="forwardButtonAction" /> @clickEvent="forwardButtonAction" />

View file

@ -86,52 +86,14 @@
.btn-link { .btn-link {
color: $link; color: $link;
} }
.btn {
&.btn-override[aria-disabled="false"]:not(.form-test-invalid) {
&.btn-primary {
background: $button-color;
color: $button-text-color;
}
&:focus-visible {
background: $button-color;
color: $button-text-color;
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color;
}
}
}
} }
&.modal-open { .btn {
.modal { &.btn-override {
.modal-body { @include heritage-btn-variant('success', $button-text-color, $button-color, transparent, true);
.btn { @include heritage-btn-variant('navigation', $button-text-color, $button-color, transparent, true);
&.btn-override[aria-disabled="false"]:not(.form-test-invalid) {
&.btn-primary {
background: $button-color;
color: $button-text-color;
}
&:focus-visible {
background: $button-color;
color: $button-text-color;
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color;
}
}
}
}
.modal-footer {
.btn:not(.navigation-link) {
background: $button-color;
color: $button-text-color;
}
}
}
} }
}
} }
// End Amica // End Amica
@ -171,52 +133,14 @@
a.new-window-link { a.new-window-link {
color: $link; color: $link;
} }
.btn {
&.btn-override[aria-disabled="false"]:not(.form-test-invalid) {
&.btn-primary {
background: $button-color;
color: $button-text-color;
}
&:focus-visible {
background: $button-color;
color: $button-text-color;
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color;
}
}
}
} }
&.modal-open { .btn {
.modal { &.btn-override {
.modal-body { @include heritage-btn-variant('success', $button-text-color, $button-color, transparent, true);
.btn { @include heritage-btn-variant('navigation', $button-text-color, $button-color, transparent, true);
&.btn-override[aria-disabled="false"]:not(.form-test-invalid) {
&.btn-primary {
background: $button-color;
color: $button-text-color;
}
&:focus-visible {
background: $button-color;
color: $button-text-color;
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color;
}
}
}
}
.modal-footer {
.btn:not(.navigation-link) {
background: $button-color;
color: $button-text-color;
}
}
}
} }
}
} }
// End Nationwide // End Nationwide
@ -256,52 +180,14 @@
a.new-window-link { a.new-window-link {
color: $link; color: $link;
} }
.btn {
&.btn-override[aria-disabled="false"]:not(.form-test-invalid) {
&.btn-primary {
background: $button-color;
color: $button-text-color;
}
&:focus-visible {
background: $button-color;
color: $button-text-color;
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color;
}
}
}
} }
&.modal-open { .btn {
.modal { &.btn-override {
.modal-body { @include heritage-btn-variant('success', $button-text-color, $button-color, transparent, true);
.btn { @include heritage-btn-variant('navigation', $button-text-color, $button-color, transparent, true);
&.btn-override[aria-disabled="false"]:not(.form-test-invalid) {
&.btn-primary {
background: $button-color;
color: $button-text-color;
}
&:focus-visible {
background: $button-color;
color: $button-text-color;
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color;
}
}
}
}
.modal-footer {
.btn:not(.navigation-link) {
background: $button-color;
color: $button-text-color;
}
}
}
} }
}
} }
// End Country Financial // End Country Financial
@ -341,52 +227,14 @@
a.new-window-link { a.new-window-link {
color: $link; color: $link;
} }
.btn {
&.btn-override[aria-disabled="false"]:not(.form-test-invalid) {
&.btn-primary {
background: $button-color;
color: $button-text-color;
}
&:focus-visible {
background: $button-color;
color: $button-text-color;
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color;
}
}
}
} }
&.modal-open { .btn {
.modal { &.btn-override {
.modal-body { @include heritage-btn-variant('success', $button-text-color, $button-color, transparent, true);
.btn { @include heritage-btn-variant('navigation', $button-text-color, $button-color, transparent, true);
&.btn-override[aria-disabled="false"]:not(.form-test-invalid) {
&.btn-primary {
background: $button-color;
color: $button-text-color;
}
&:focus-visible {
background: $button-color;
color: $button-text-color;
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color;
}
}
}
}
.modal-footer {
.btn:not(.navigation-link) {
background: $button-color;
color: $button-text-color;
}
}
}
} }
}
} }
// End Travelers // End Travelers

View file

@ -182,4 +182,50 @@ body {
:root { :root {
--iss-loader-color: white; --iss-loader-color: white;
}
//Button Styles
$heritage-btn-border-radius: 1.40625rem; // 22.5px
$heritage-btn-height: 2.8125rem; // 45px
$heritage-btn-width: 9.0625rem; // 145px
.btn {
position: relative;
--bs-btn-font-weight: #{$font-weight-bolder};
min-height: $heritage-btn-height;
min-width: $heritage-btn-width;
letter-spacing: inherit;
line-height: .94;
@include heritage-btn-variant('primary', $white, $heritage-blue-primary, transparent, true);
@include heritage-btn-variant('success', $white, $green, transparent, true);
@include heritage-btn-variant('navigation', $white, $green, transparent, true);
@include heritage-btn-variant('secondary', $white, $heritage-blue-secondary, transparent, true);
@include heritage-btn-link('link', $blue, transparent, $heritage-blue-secondary);
@include heritage-btn-size('lg', 0.625rem, 1.25rem, 20px, $heritage-btn-border-radius);
@include heritage-btn-size('md', 7px, 15px, 15px, $heritage-btn-border-radius);
@include heritage-btn-size('sm', 3px, 6px, 12px, $heritage-btn-border-radius);
@include heritage-btn-size('xs', 1px, 5px, 12px, $heritage-btn-border-radius);
&.btn-link {
--bs-btn-padding-x: 0px;
text-decoration: none;
&:hover,
&:focus {
text-decoration: underline;
}
&.disabled,
&[disabled],
fieldset[disabled] {
text-decoration: none;
}
}
&.delay {
// fixes flicker while transitioning between states
transition: background 0s 0s ease-in-out;
}
} }

View file

@ -8,3 +8,54 @@
@mixin box-shadow-hover($color) { @mixin box-shadow-hover($color) {
box-shadow: 0 0 0 4px $color; box-shadow: 0 0 0 4px $color;
} }
@mixin heritage-btn-box-shadow($color) {
box-shadow:0 0 0 3px $white,0 0 0 5.5px $color
}
@mixin heritage-btn-variant($name, $color, $background, $border-color, $box-shadow) {
&.btn-#{$name} {
@include button-variant($background,
$border-color,
$color,
#{darken($background, 10%)},
$border-color,
$color,
$background,
$border-color,
$color,
$background,
$border-color,
$color
);
@if($box-shadow) {
&:focus, &:active, &:active:focus {
@include heritage-btn-box-shadow($background);
}
}
}
&.btn-outline-#{$name} {
@include button-outline-variant($background,
$color,
$background,
$color,
$background
$color,
);
}
}
@mixin heritage-btn-size($size, $y-padding, $x-padding, $font-size, $border-radius) {
&.btn-#{$size} {
@include button-size($y-padding, $x-padding, $font-size, $border-radius);
}
}
@mixin heritage-btn-link($name, $color, $background, $hover-color) {
&.btn-#{$name} {
--bs-btn-color: #{$color};
--bs-btn-bg: #{$background};
--bs-btn-hover-color: #{$hover-color};
--bs-btn-active-color: #{$hover-color};
}
}

View file

@ -1,6 +1,7 @@
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { getMountOptions } from '@/helpers/unit-test-helper.js'; import { getMountOptions } from '@/helpers/unit-test-helper.js';
import buttonMain from '@/ux-components/button-main/button-main.vue'; import buttonMain from '@/ux-components/button-main/button-main.vue';
import buttonVariants from '@/constants/button-variants';
/** @ignore */ /** @ignore */
function setupMocks(mountOptionsMockData = {}) { function setupMocks(mountOptionsMockData = {}) {
@ -12,13 +13,75 @@ function setupMocks(mountOptionsMockData = {}) {
} }
describe('buttonMain.vue', () => { describe('buttonMain.vue', () => {
it('Should return btn-primary class', async () => { describe.each([...Object.keys(buttonVariants)])('Button Variants', (variant) => {
test(`Should return btn-${variant} class`, () => {
// Act
const wrapper = shallowMount(
buttonMain,
setupMocks({
propsData: {
variant
}
})
);
// Assert
const button = wrapper.find('button');
// Expect
expect(button.attributes('class')).toContain(`btn-${variant}`);
});
});
describe.each([...Object.keys(buttonVariants)])('Button Outline Variants', (variant) => {
test(`Should return btn-outline-${variant} class`, () => {
// Act
const wrapper = shallowMount(
buttonMain,
setupMocks({
propsData: {
variant,
isOutline: true
}
})
);
// Assert
const button = wrapper.find('button');
// Expect
expect(button.attributes('class')).toContain(`btn-outline-${variant}`);
});
});
describe.each(['', 'sm', 'lg'])('Button Sizes', (size) => {
test(`Should return btn-${size} class`, () => {
// Act
const wrapper = shallowMount(
buttonMain,
setupMocks({
propsData: {
size
}
})
);
// Assert
const button = wrapper.find('button');
// Expect
expect(button.attributes('class')).toContain(`btn-${size}`);
});
});
it('Should return button text', async () => {
// Act // Act
const buttonText = 'Test Text';
const wrapper = shallowMount( const wrapper = shallowMount(
buttonMain, buttonMain,
setupMocks({ setupMocks({
propsData: { propsData: {
isPrimary: true buttonText
} }
}) })
); );
@ -27,10 +90,10 @@ describe('buttonMain.vue', () => {
const button = wrapper.find('button'); const button = wrapper.find('button');
// Expect // Expect
expect(button.attributes('class')).toContain('btn-primary'); expect(button.text()).toContain(buttonText);
}); });
it('Should return aria-disabled state', async () => { it('Should return disabled state', async () => {
// Act // Act
const wrapper = shallowMount( const wrapper = shallowMount(
buttonMain, buttonMain,
@ -47,4 +110,85 @@ describe('buttonMain.vue', () => {
// Expect // Expect
expect(button.attributes()['aria-disabled']).toEqual('true'); expect(button.attributes()['aria-disabled']).toEqual('true');
}); });
it('Should emit click-event', async () => {
// Act
const wrapper = shallowMount(
buttonMain,
setupMocks({
propsData: {
isDisabled: false
}
})
);
// Assert
const button = wrapper.find('button');
await button.trigger('click');
// Expect
expect(wrapper.emitted('click').length).toBe(1);
});
it('Should not emit click-event when disabled', async () => {
// Act
const wrapper = shallowMount(
buttonMain,
setupMocks({
propsData: {
isDisabled: true
}
})
);
// Assert
const button = wrapper.find('button');
await button.trigger('click');
// Expect
expect(wrapper.emitted('click').length).toBe(1);
});
it('Should emit GA event when clicked', async () => {
// Act
const wrapper = shallowMount(
buttonMain,
setupMocks({
propsData: {
pushToGA: true
}
})
);
const spy = jest.spyOn(wrapper.vm, 'pushEventToGA');
// Assert
const button = wrapper.find('button');
await button.trigger('click');
// Expect
expect(spy).toHaveBeenCalled();
});
it('Should not emit GA event when clicked', async () => {
// Act
const wrapper = shallowMount(
buttonMain,
setupMocks({
propsData: {
pushToGA: false
}
})
);
const spy = jest.spyOn(wrapper.vm, 'pushEventToGA');
// Assert
const button = wrapper.find('button');
await button.trigger('click');
// Expect
expect(spy).not.toHaveBeenCalled();
});
}); });

View file

@ -1,30 +1,49 @@
<template> <template>
<button <button
:aria-disabled="isDisabled" :aria-disabled="isDisabled"
class="btn d-flex btn-override align-items-center justify-content-center py-3 px-4 delay" class="btn d-flex align-items-center justify-content-center delay"
:class="[ :class="[getVariant, `btn-${size}`, canOverride ? 'btn-override' : '']"
isPrimary ? 'btn-primary' : 'btn-secondary',
isFloat ? 'float-end' : '',
]"
@click="clicked"> @click="clicked">
<span class="m-0">{{ buttonText }}</span> <span class="m-0">{{ buttonText }}</span>
</button> </button>
</template> </template>
<script> <script>
import buttonVariants from '@/constants/button-variants';
import buttonSizes from '@/constants/button-sizes';
export default { export default {
name: 'button-main', name: 'button-main',
props: { props: {
isPrimary: Boolean, variant: { type: String, default: 'primary', validator: (val) => Object.keys(buttonVariants).includes(val) },
size: { type: String, default: 'md', validator: (val) => Object.keys(buttonSizes).includes(val) },
buttonText: String, buttonText: String,
isDisabled: Boolean, isDisabled: Boolean,
loaderPosition: String, isOutline: Boolean,
isFloat: Boolean, pushToGA: Boolean,
suppressLoader: Boolean canOverride: { type: Boolean, required: false, default: true }
}, },
emits: ['click-event'], emits: ['click-event'],
computed: {
getVariant() {
if (this.isOutline) {
return `btn-outline-${this.variant}`;
}
return `btn-${this.variant}`;
}
},
methods: { methods: {
clicked() { clicked() {
if (this.pushToGA) {
this.pushEventToGA(
this.$route.query[this.queryStrings.ISS_PAGE],
this.GaActions.CLICKED,
this.buttonText,
true
);
}
if (!this.isDisabled) { if (!this.isDisabled) {
this.$emit('click-event'); this.$emit('click-event');
} }
@ -34,86 +53,5 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
$border-radius-heritage: 1.40625rem; // 22.5px
$button-height-heritage: 2.8125rem; // 45px
$button-width-heritage: 9.0625rem; // 145px
.btn {
&.btn-primary {
position: relative;
background: #0c7e47;
padding-left: 2rem;
padding-right: 2rem;
min-height: $button-height-heritage;
min-width: $button-width-heritage;
border: none;
border-radius: $border-radius-heritage;
color: $white;
justify-content: center;
font-weight: 600;
letter-spacing: inherit;
&:focus {
// Keyboard focus for accessibility
outline: none;
box-shadow: 0 0 0 3px #fff, 0 0 0 5.5px #0c7e47;
}
&:hover {
// Keyboard focus for accessibility
color: #fff;
background-color: #084f2d;
border-color: #042f1a;
}
&:active {
outline: none;
background-color: #0c7e47;
box-shadow: 0 0 0 3px #fff, 0 0 0 5.5px #0c7e47;
}
&:disabled {
background: red;
color: $gray-600 !important;
border: none;
border-radius: $border-radius-heritage;
cursor: pointer;
pointer-events: all;
}
&.delay {
// fixes flicker while transitioning between states
transition: background 0s 0s ease-in-out;
}
}
&.btn-secondary {
position: relative;
background: transparent;
border: 1px solid $blue;
border-radius: $border-radius-heritage;
color: $blue;
font-weight: 500;
transition: all 150ms linear;
&:hover {
color: $white;
@include blue-gradient;
}
&:focus, // Mouse, touch, stylus focus
&:focus-visible {
// Keyboard focus for accessibility
outline: none;
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $blue-700;
color: $white;
@include blue-gradient;
}
&:disabled {
background: transparent;
color: $gray-550 !important;
font-weight: 400;
border: 1px solid $gray-550;
border-radius: $border-radius-heritage;
cursor: pointer;
pointer-events: all;
}
&.delay {
// fixes flicker while transitioning between states
transition: background 0s 0s ease-in-out;
}
}
}
</style> </style>

View file

@ -1,95 +0,0 @@
import { shallowMount } from '@vue/test-utils';
import { getMountOptions } from '@/helpers/unit-test-helper.js';
import { nextTick } from 'vue';
import modalButtonMain from '@/ux-components/modal-button-main/modal-button-main.vue';
/** @ignore */
function setupMocks(mountOptionsMockData = {}) {
const defaultMountOptions = { route: { query: { issPage: 'page-name' } } };
const baseMountOptions = getMountOptions(Object.assign(defaultMountOptions, mountOptionsMockData));
const allMountOptions = Object.assign(defaultMountOptions, baseMountOptions);
return allMountOptions;
}
describe('modal-button-main.vue', () => {
it('Should return btn-primary class', () => {
// Arrange/Act
const wrapper = shallowMount(
modalButtonMain,
setupMocks({
propsData: {
isPrimary: true
}
})
);
const button = wrapper.find('button');
// Assert
expect(button.attributes('class')).toContain('btn-primary');
});
it('Should return aria-disabled state', () => {
// Arrange/Act
const wrapper = shallowMount(
modalButtonMain,
setupMocks({
propsData: {
isDisabled: true
}
})
);
const button = wrapper.find('button');
// Assert
expect(button.attributes()['aria-disabled']).toEqual('true');
});
it("Should emit 'click-event' event when clicking if the button is enabled", async () => {
// Arrange
const wrapper = shallowMount(
modalButtonMain,
setupMocks({
propsData: {
loaderPosition: 'right',
loaderEnabled: true,
isDisabled: false
}
})
);
const buttonElement = wrapper.find('button');
// Act
buttonElement.trigger('click');
await nextTick();
// Assert
expect(wrapper.emitted('click-event')).toBeTruthy();
});
it("Should not emit 'click-event' event when clicking if the button is disabled", async () => {
// Arrange
const wrapper = shallowMount(
modalButtonMain,
setupMocks({
propsData: {
loaderPosition: 'right',
loaderEnabled: true,
isDisabled: true
}
})
);
const buttonElement = wrapper.find('button');
// Act
buttonElement.trigger('click');
await nextTick();
// Assert
expect(wrapper.emitted('click-event')).toBeFalsy();
});
});

View file

@ -1,121 +0,0 @@
<template>
<button
type="button"
:aria-disabled="isDisabled"
class="btn d-flex btn-override align-items-center justify-content-center py-3 px-4 delay"
:class="[
isPrimary ? 'btn-primary' : 'btn-secondary',
isFloat ? 'float-end' : '',
]"
@click="clicked">
<span class="m-0">{{ buttonText }}</span>
</button>
</template>
<script>
export default {
name: 'modal-button-main',
props: {
isPrimary: Boolean,
buttonText: String,
isDisabled: Boolean,
loaderColor: String,
loaderPosition: String,
isFloat: Boolean,
suppressLoader: Boolean
},
emits: ['click-event'],
methods: {
clicked() {
this.pushEventToGA(
this.$route.query[this.queryStrings.ISS_PAGE],
this.GaActions.CLICKED,
this.buttonText,
true
);
if (!this.isDisabled) {
this.$emit('click-event');
}
}
}
};
</script>
<style lang="scss" scoped>
.btn {
&.btn-primary {
position: relative;
background: linear-gradient(270deg, $blue 0%, $blue-800 100%);
border: none;
border-radius: $border-radius-lg;
color: $white;
justify-content: center;
font-weight: 500;
letter-spacing: inherit;
@media (hover: hover) {
background: linear-gradient(270deg, $blue 0%, $blue-800 100%);
}
// Mouse, touch, stylus focus
&:focus-visible {
// Keyboard focus for accessibility
outline: none;
box-shadow: 0 0 0 3px, 0 0 0 5.5px $blue-700;
color: $white;
background: linear-gradient(270deg, rgba(6, 87, 124, 1) 0%, rgba(6, 87, 124, 1) 100%);
}
&:disabled {
background: $gray-200 !important;
background: linear-gradient(270deg, $gray-200 0%, $gray-200 100%) !important;
color: $gray-600 !important;
font-weight: 400;
height: 48px;
border: none;
border-radius: $border-radius-lg;
cursor: pointer;
pointer-events: all;
}
&.delay {
// fixes flicker while transitioning between states
transition: background 0s 0s ease-in-out;
}
}
&.btn-secondary {
position: relative;
background: transparent;
border: 1px solid $blue;
border-radius: $border-radius-lg;
color: $blue;
font-weight: 500;
transition: all 150ms linear;
height: 3rem;
&:hover {
color: $white;
@include blue-gradient;
}
&:focus, // Mouse, touch, stylus focus
&:focus-visible {
// Keyboard focus for accessibility
outline: none;
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $blue-700;
color: $white;
@include blue-gradient;
}
&:disabled {
background: transparent;
color: $gray-550 !important;
font-weight: 400;
height: 48px;
border: 1px solid $gray-550;
border-radius: $border-radius-lg;
cursor: pointer;
pointer-events: all;
}
&.delay {
// fixes flicker while transitioning between states
transition: background 0s 0s ease-in-out;
}
}
}
</style>