Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions src/components/base-address/BaseAddress.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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. */
Expand All @@ -358,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. */
Expand Down
10 changes: 9 additions & 1 deletion src/validators/is-required-postal-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,21 @@ 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
Expand Down
108 changes: 108 additions & 0 deletions tests/unit/BaseAddress.spec.ts
Original file line number Diff line number Diff line change
@@ -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<any>

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)
})
})
Loading