From 32405df13c673e936c74833d8f4e6b954c027682 Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 10 Feb 2026 13:12:59 -0800 Subject: [PATCH 1/3] Contingent required label Signed-off-by: Lucas --- src/components/base-address/BaseAddress.vue | 11 +++++++++-- src/validators/is-required-postal-code.ts | 12 ++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/components/base-address/BaseAddress.vue b/src/components/base-address/BaseAddress.vue index 980de099..e40e890a 100644 --- a/src/components/base-address/BaseAddress.vue +++ b/src/components/base-address/BaseAddress.vue @@ -165,7 +165,7 @@ import { Validations } from 'vuelidate-property-decorators' import { uniqueId } from 'lodash' import { ValidationMixin, CountriesProvincesMixin } from '@bcrs-shared-components/mixins' import { FormIF } from '@bcrs-shared-components/interfaces' - +import { isPostalCodeOptionalForCountry } from '@/validators' /** * The component for displaying and editing an address. * Vuelidate is used to implement the validation rules (eg, what 'required' means and whether it's satisfied). @@ -335,7 +335,14 @@ export default class BaseAddress extends Mixins(ValidationMixin, CountriesProvin } else { label = 'Postal Code' } - return label + (this.isSchemaRequired('postalCode') ? '' : ' (Optional)') + + let isRequired = this.isSchemaRequired('postalCode') + // If schema uses isRequiredPostalCode validator, check if country requires postal codes + if (!isRequired && this.schemaLocal?.postalCode?.isRequiredPostalCode) { + isRequired = !isPostalCodeOptionalForCountry(this.addressCountry) + } + + return label + (isRequired ? '' : ' (Optional)') } /** The Address Country label with 'optional' as needed. */ diff --git a/src/validators/is-required-postal-code.ts b/src/validators/is-required-postal-code.ts index 5ef87062..66ed3e68 100644 --- a/src/validators/is-required-postal-code.ts +++ b/src/validators/is-required-postal-code.ts @@ -11,15 +11,23 @@ const NO_POSTAL_CODE_COUNTRY_CODES = new Set([ 'TL', 'TG', 'TK', 'TO', 'TT', 'TV', 'UG', 'AE', 'VU', 'YE', 'ZW' ]) +/** + * Returns true if the given country does not use postal codes. + * @param countryCode the ISO 3166-1 alpha-2 country code + */ +export function isPostalCodeOptionalForCountry (countryCode: string): boolean { + return NO_POSTAL_CODE_COUNTRY_CODES.has(countryCode) +} + /** * Custom validator for postal code required. * Returns true if postal code has a value, or if the country doesn't use postal codes. */ export function isRequiredPostalCode (value: string, parentVm: any): boolean { // If the country doesn't use postal codes, no value is required - if (NO_POSTAL_CODE_COUNTRY_CODES.has(parentVm.addressCountry)) { + if (isPostalCodeOptionalForCountry(parentVm.addressCountry)) { return true } // Otherwise, postal code is required return !!value?.trim() -} +} \ No newline at end of file From 8d6e6a14be826cd0793556230821e925fced7ba4 Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 10 Feb 2026 13:33:14 -0800 Subject: [PATCH 2/3] Lint Signed-off-by: Lucas --- src/validators/is-required-postal-code.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/validators/is-required-postal-code.ts b/src/validators/is-required-postal-code.ts index 66ed3e68..27cbdb5f 100644 --- a/src/validators/is-required-postal-code.ts +++ b/src/validators/is-required-postal-code.ts @@ -30,4 +30,4 @@ export function isRequiredPostalCode (value: string, parentVm: any): boolean { } // Otherwise, postal code is required return !!value?.trim() -} \ No newline at end of file +} From f6a104b386ff556033a9217ffdca981f37bbfebf Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 10 Feb 2026 13:55:04 -0800 Subject: [PATCH 3/3] Adjust syntax on isSchemaRequired Signed-off-by: Lucas --- src/components/base-address/BaseAddress.vue | 2 +- tests/unit/BaseAddress.spec.ts | 108 ++++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 tests/unit/BaseAddress.spec.ts diff --git a/src/components/base-address/BaseAddress.vue b/src/components/base-address/BaseAddress.vue index e40e890a..008ef20b 100644 --- a/src/components/base-address/BaseAddress.vue +++ b/src/components/base-address/BaseAddress.vue @@ -365,7 +365,7 @@ export default class BaseAddress extends Mixins(ValidationMixin, CountriesProvin /** Whether the specified prop is required according to the schema. */ isSchemaRequired (prop: string): boolean { - return Boolean(this.schemaLocal && this.schemaLocal[prop] && this.schemaLocal[prop].required) + return Boolean(this.schemaLocal?.[prop]?.required) } /** Array of validation rules used by input elements to prevent extra whitespace. */ diff --git a/tests/unit/BaseAddress.spec.ts b/tests/unit/BaseAddress.spec.ts new file mode 100644 index 00000000..ea6437d9 --- /dev/null +++ b/tests/unit/BaseAddress.spec.ts @@ -0,0 +1,108 @@ +import Vue from 'vue' +import Vuetify from 'vuetify' +import { mount, Wrapper } from '@vue/test-utils' +import { BaseAddress } from '@/components/base-address' + +const vuetify = new Vuetify({}) + +describe('BaseAddress - isSchemaRequired', () => { + let wrapper: Wrapper + + afterEach(() => { + wrapper.destroy() + }) + + it('returns true when prop has required validator', () => { + const schema = { + streetAddress: { required: true }, + postalCode: { isRequiredPostalCode: true } + } + + wrapper = mount(BaseAddress, { + vuetify, + propsData: { + schema, + editing: false + } + }) + + expect(wrapper.vm.isSchemaRequired('streetAddress')).toBe(true) + }) + + it('returns false when prop does not have required validator', () => { + const schema = { + streetAddressAdditional: { maxLength: 50 }, + postalCode: { isRequiredPostalCode: true } + } + + wrapper = mount(BaseAddress, { + vuetify, + propsData: { + schema, + editing: false + } + }) + + expect(wrapper.vm.isSchemaRequired('streetAddressAdditional')).toBe(false) + }) + + it('returns false when prop has isRequiredPostalCode but not required', () => { + const schema = { + postalCode: { isRequiredPostalCode: true } + } + + wrapper = mount(BaseAddress, { + vuetify, + propsData: { + schema, + editing: false + } + }) + + expect(wrapper.vm.isSchemaRequired('postalCode')).toBe(false) + }) + + it('returns false when schema is null', () => { + wrapper = mount(BaseAddress, { + vuetify, + propsData: { + schema: null, + editing: false + } + }) + + expect(wrapper.vm.isSchemaRequired('streetAddress')).toBe(false) + }) + + it('returns false when prop does not exist in schema', () => { + const schema = { + streetAddress: { required: true } + } + + wrapper = mount(BaseAddress, { + vuetify, + propsData: { + schema, + editing: false + } + }) + + expect(wrapper.vm.isSchemaRequired('nonExistentProp')).toBe(false) + }) + + it('returns false when required is false', () => { + const schema = { + streetAddress: { required: false } + } + + wrapper = mount(BaseAddress, { + vuetify, + propsData: { + schema, + editing: false + } + }) + + expect(wrapper.vm.isSchemaRequired('streetAddress')).toBe(false) + }) +})