diff --git a/btr-web/btr-common-components/app.config.ts b/btr-web/btr-common-components/app.config.ts index cd366c00..06560b40 100644 --- a/btr-web/btr-common-components/app.config.ts +++ b/btr-web/btr-common-components/app.config.ts @@ -2,6 +2,11 @@ export default defineAppConfig({ ui: { primary: 'bcGovBlue', gray: 'bcGovGray', + button: { + variant: { + solid: 'hover:bg-opacity-[.92] hover:bg-{color}-500' + } + }, formGroup: { label: { base: 'block text-base font-bold py-3 text-gray-900' } }, diff --git a/btr-web/btr-common-components/lang/en.json b/btr-web/btr-common-components/lang/en.json index b28afff7..0a0fd6d2 100644 --- a/btr-web/btr-common-components/lang/en.json +++ b/btr-web/btr-common-components/lang/en.json @@ -18,6 +18,12 @@ "registrationNum": "Registration Number" }, "labels": { + "buttons": { + "cancel": "Cancel", + "reviewConfirm": "Review and Confirm", + "save": "Save", + "saveExit": "Save and Resume Later" + }, "birthdate": "Birthdate", "competency": "Competency", "citizenship": "Citizenship", diff --git a/btr-web/btr-layouts/components/bcros/Breadcrumb.vue b/btr-web/btr-layouts/components/bcros/Breadcrumb.vue index 2a27d338..4424fc27 100644 --- a/btr-web/btr-layouts/components/bcros/Breadcrumb.vue +++ b/btr-web/btr-layouts/components/bcros/Breadcrumb.vue @@ -51,81 +51,3 @@ const navigate = (breadcrumb: BreadcrumbI): void => { } - - diff --git a/btr-web/btr-layouts/components/bcros/BusinessDetails.vue b/btr-web/btr-layouts/components/bcros/BusinessDetails.vue index 0c2e6b77..494d3172 100644 --- a/btr-web/btr-layouts/components/bcros/BusinessDetails.vue +++ b/btr-web/btr-layouts/components/bcros/BusinessDetails.vue @@ -51,8 +51,8 @@ function loadComponentData (identifier: string) { } // watcher required because layouts start rendering before the route is initialized watch(() => route.params.identifier as string, loadComponentData) -onMounted(() => { - // onMounted required for refresh case (route will be set already so ^ watcher will not fire) +onBeforeMount(() => { + // onBeforeMount required for refresh case (route will be set already so ^ watcher will not fire) if (route.params.identifier) { loadComponentData(route.params.identifier as string) } diff --git a/btr-web/btr-layouts/components/bcros/ButtonControl.vue b/btr-web/btr-layouts/components/bcros/ButtonControl.vue new file mode 100644 index 00000000..2d467876 --- /dev/null +++ b/btr-web/btr-layouts/components/bcros/ButtonControl.vue @@ -0,0 +1,46 @@ + + + diff --git a/btr-web/btr-layouts/interfaces/button-control-i.ts b/btr-web/btr-layouts/interfaces/button-control-i.ts new file mode 100644 index 00000000..da0223d6 --- /dev/null +++ b/btr-web/btr-layouts/interfaces/button-control-i.ts @@ -0,0 +1,8 @@ +export interface ButtonControlI { + action: () => any + color?: string + icon?: string + label: string + variant?: string + trailing?: boolean +} diff --git a/btr-web/btr-layouts/layouts/business.vue b/btr-web/btr-layouts/layouts/business.vue index 560eb039..5b0bac78 100644 --- a/btr-web/btr-layouts/layouts/business.vue +++ b/btr-web/btr-layouts/layouts/business.vue @@ -6,6 +6,10 @@
+ @@ -13,6 +17,12 @@ diff --git a/btr-web/btr-layouts/tests/unit/components/bcros/ButtonControl.spec.ts b/btr-web/btr-layouts/tests/unit/components/bcros/ButtonControl.spec.ts new file mode 100644 index 00000000..4805684c --- /dev/null +++ b/btr-web/btr-layouts/tests/unit/components/bcros/ButtonControl.spec.ts @@ -0,0 +1,64 @@ +import { describe, expect, it, vi } from 'vitest' +import { VueWrapper, mount } from '@vue/test-utils' + +import { BcrosButtonControl } from '#components' + +describe('Button Control tests', () => { + let wrapper: VueWrapper + + const leftBtnActions = [ + vi.fn().mockImplementation(() => {}), + vi.fn().mockImplementation(() => {}), + vi.fn().mockImplementation(() => {}) + ] + const rightBtnActions = [vi.fn().mockImplementation(() => {}), vi.fn().mockImplementation(() => {})] + + const leftButtons: ButtonControlI[] = [ + { action: leftBtnActions[0], label: 'left 1' }, + { action: leftBtnActions[1], label: 'left 2' }, + { action: leftBtnActions[2], label: 'left 3' } + ] + + const rightButtons: ButtonControlI[] = [ + { action: rightBtnActions[0], label: 'right 1' }, + { action: rightBtnActions[1], label: 'right 2' } + ] + + beforeEach(() => { + wrapper = mount( + BcrosButtonControl, + { + props: { + leftButtonConstructors: leftButtons.map(btn => () => btn), + rightButtonConstructors: rightButtons.map(btn => () => btn) + } + }) + }) + afterEach(() => { + wrapper.unmount() + vi.clearAllMocks() + }) + + it('renders with expected buttons / behaviour', async () => { + expect(wrapper.find('#bcros-button-control').exists()).toBe(true) + + const renderedLeftBtns = wrapper.findAll('[data-cy=button-control-left-button]') + const renderedRightBtns = wrapper.findAll('[data-cy=button-control-right-button]') + expect(renderedLeftBtns.length).toBe(leftButtons.length) + expect(renderedRightBtns.length).toBe(rightButtons.length) + + for (let i = 0; i < leftButtons.length; i++) { + expect(renderedLeftBtns.at(i)?.text()).toBe(leftButtons[i].label) + expect(leftBtnActions[i]).not.toHaveBeenCalled() + await renderedLeftBtns.at(i)?.trigger('click') + expect(leftBtnActions[i]).toHaveBeenCalled() + } + + for (let i = 0; i < rightButtons.length; i++) { + expect(renderedRightBtns.at(i)?.text()).toBe(rightButtons[i].label) + expect(rightBtnActions[i]).not.toHaveBeenCalled() + await renderedRightBtns.at(i)?.trigger('click') + expect(rightBtnActions[i]).toHaveBeenCalled() + } + }) +}) diff --git a/btr-web/btr-main-app/app/router.options.ts b/btr-web/btr-main-app/app/router.options.ts index 122fccf3..090b6a4d 100644 --- a/btr-web/btr-main-app/app/router.options.ts +++ b/btr-web/btr-main-app/app/router.options.ts @@ -17,6 +17,10 @@ export default { getBusinessNameCrumb, getBeneficialOwnerChangeCrumb ], + buttonControl: { + leftButtons: [getSIChangeCancel, getSIChangeSaveExit, getSIChangeSave], + rightButtons: [getSIChangeConfirm] + }, layout: 'business', title: 'Beneficial Owner Change' } diff --git a/btr-web/btr-main-app/cypress/e2e/layouts/buttonControl.cy.ts b/btr-web/btr-main-app/cypress/e2e/layouts/buttonControl.cy.ts new file mode 100644 index 00000000..6d43006b --- /dev/null +++ b/btr-web/btr-main-app/cypress/e2e/layouts/buttonControl.cy.ts @@ -0,0 +1,18 @@ +describe('Layout -> ButtonControl', () => { + it('shows button control in the business layout for SI change', () => { + cy.visit('/') + cy.wait(1000) + cy.get('#bcros-button-control').should('exist') + cy.get('[data-cy=button-control-left-button]').should('have.length', 3) + cy.get('[data-cy=button-control-left-button]').eq(0).should('have.text', 'Cancel') + cy.get('[data-cy=button-control-left-button]').eq(1).should('have.text', 'Save and Resume Later') + cy.get('[data-cy=button-control-left-button]').eq(2).should('have.text', 'Save') + cy.get('[data-cy=button-control-right-button]').should('have.length', 1) + cy.get('[data-cy=button-control-right-button]').eq(0).should('have.text', 'Review and Confirm') + }) + + it('does NOT show button control in the person layout for profile', () => { + cy.visit('/my-registries-details') + cy.get('#bcros-button-control').should('not.exist') + }) +}) diff --git a/btr-web/btr-main-app/utils/button-controls.ts b/btr-web/btr-main-app/utils/button-controls.ts new file mode 100644 index 00000000..cd2e2956 --- /dev/null +++ b/btr-web/btr-main-app/utils/button-controls.ts @@ -0,0 +1,33 @@ +// FUTURE: pass action functions from SI store +export function getSIChangeCancel (): ButtonControlI { + return { + action: () => {}, + label: useI18n().t('labels.buttons.cancel'), + variant: 'outline' + } +} + +export function getSIChangeConfirm (): ButtonControlI { + return { + action: () => {}, + icon: 'i-mdi-chevron-right', + label: useI18n().t('labels.buttons.reviewConfirm'), + trailing: true + } +} + +export function getSIChangeSave (): ButtonControlI { + return { + action: () => {}, + label: useI18n().t('labels.buttons.save'), + variant: 'outline' + } +} + +export function getSIChangeSaveExit (): ButtonControlI { + return { + action: () => {}, + label: useI18n().t('labels.buttons.saveExit'), + variant: 'outline' + } +}