diff --git a/.nvmrc b/.nvmrc index 8351c193..b6a7d89c 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -14 +16 diff --git a/.storybook/main.js b/.storybook/main.js index 5ab5622f..5e8636c4 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -10,6 +10,7 @@ module.exports = { addons: ['@storybook/addon-links', '@storybook/addon-essentials'], core: { builder: '@storybook/builder-vite', + disableTelemetry: true, }, framework: '@storybook/vue3', diff --git a/package-lock.json b/package-lock.json index 420b63bf..c764fb4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10328,9 +10328,9 @@ } }, "node_modules/css-loader/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "dependencies": { "minimist": "^1.2.0" @@ -14825,9 +14825,9 @@ } }, "node_modules/html-webpack-plugin/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "dependencies": { "minimist": "^1.2.0" @@ -17071,9 +17071,9 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "bin": { "json5": "lib/cli.js" @@ -28432,9 +28432,9 @@ } }, "node_modules/vue-docgen-loader/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "dependencies": { "minimist": "^1.2.0" @@ -29264,9 +29264,9 @@ } }, "node_modules/webpack/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "dependencies": { "minimist": "^1.2.0" @@ -37394,9 +37394,9 @@ }, "dependencies": { "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "requires": { "minimist": "^1.2.0" @@ -40799,9 +40799,9 @@ }, "dependencies": { "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "requires": { "minimist": "^1.2.0" @@ -42517,9 +42517,9 @@ "dev": true }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, "jsonfile": { @@ -51111,9 +51111,9 @@ }, "dependencies": { "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "requires": { "minimist": "^1.2.0" @@ -51671,9 +51671,9 @@ "dev": true }, "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "requires": { "minimist": "^1.2.0" diff --git a/package.json b/package.json index b670ec94..d43fba29 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,7 @@ }, "lint-staged": { "*.{js,vue}": [ - "eslint" + "npm run lint" ] } } diff --git a/src/components/UsaBannerContent/UsaBannerContent.test.js b/src/components/UsaBannerContent/UsaBannerContent.test.js index 8dacef38..7d965055 100644 --- a/src/components/UsaBannerContent/UsaBannerContent.test.js +++ b/src/components/UsaBannerContent/UsaBannerContent.test.js @@ -78,19 +78,19 @@ describe('UsaBannerContent', () => { cy.get('@consoleWarn').should( 'be.calledWith', - `The 'tldIcon' slot' is deprecated, use 'tld-icon' instead.` + `The 'tldIcon' slot is deprecated, use 'tld-icon' instead.` ) cy.get('@consoleWarn').should( 'be.calledWith', - `The 'tldDescription' slot' is deprecated, use 'tld-description' instead.` + `The 'tldDescription' slot is deprecated, use 'tld-description' instead.` ) cy.get('@consoleWarn').should( 'be.calledWith', - `The 'httpsIcon' slot' is deprecated, use 'https-icon' instead.` + `The 'httpsIcon' slot is deprecated, use 'https-icon' instead.` ) cy.get('@consoleWarn').should( 'be.calledWith', - `The 'httpsDescription' slot' is deprecated, use 'https-description' instead.` + `The 'httpsDescription' slot is deprecated, use 'https-description' instead.` ) cy.get('.usa-banner__guidance').should( diff --git a/src/components/UsaBannerContent/UsaBannerContent.vue b/src/components/UsaBannerContent/UsaBannerContent.vue index 590c6496..9bdcbc9f 100644 --- a/src/components/UsaBannerContent/UsaBannerContent.vue +++ b/src/components/UsaBannerContent/UsaBannerContent.vue @@ -9,22 +9,22 @@ import { const slots = useSlots() if (slots?.tldIcon) { - console.warn(`The 'tldIcon' slot' is deprecated, use 'tld-icon' instead.`) + console.warn(`The 'tldIcon' slot is deprecated, use 'tld-icon' instead.`) } if (slots?.tldDescription) { console.warn( - `The 'tldDescription' slot' is deprecated, use 'tld-description' instead.` + `The 'tldDescription' slot is deprecated, use 'tld-description' instead.` ) } if (slots?.httpsIcon) { - console.warn(`The 'httpsIcon' slot' is deprecated, use 'https-icon' instead.`) + console.warn(`The 'httpsIcon' slot is deprecated, use 'https-icon' instead.`) } if (slots?.httpsDescription) { console.warn( - `The 'httpsDescription' slot' is deprecated, use 'https-description' instead.` + `The 'httpsDescription' slot is deprecated, use 'https-description' instead.` ) } diff --git a/src/components/UsaDateInput/UsaDateInput.stories.js b/src/components/UsaDateInput/UsaDateInput.stories.js index c16bbe2e..2d272496 100644 --- a/src/components/UsaDateInput/UsaDateInput.stories.js +++ b/src/components/UsaDateInput/UsaDateInput.stories.js @@ -7,6 +7,9 @@ const defaultProps = { year: UsaDateInput.props.year.default, dateOrder: UsaDateInput.props.dateOrder.default(), dateLabels: UsaDateInput.props.dateLabels.default(), + monthAsSelect: UsaDateInput.props.monthAsSelect.default, + monthEmptyLabel: UsaDateInput.props.monthEmptyLabel.default, + monthOptions: UsaDateInput.props.monthOptions.default(), name: UsaDateInput.props.name.default, required: UsaDateInput.props.required.default, error: UsaDateInput.props.error.default, @@ -38,6 +41,15 @@ export default { dateLabels: { control: { type: 'object' }, }, + monthAsSelect: { + control: { type: 'boolean' }, + }, + monthEmptyLabel: { + control: { type: 'text' }, + }, + monthOptions: { + control: { type: 'object' }, + }, name: { control: { type: 'text' }, }, @@ -71,6 +83,9 @@ export default { year: defaultProps.year, dateOrder: defaultProps.dateOrder, dateLabels: defaultProps.dateLabels, + monthAsSelect: defaultProps.monthAsSelect, + monthEmptyLabel: defaultProps.monthEmptyLabel, + monthOptions: defaultProps.monthOptions, name: defaultProps.name, required: defaultProps.required, error: defaultProps.error, @@ -99,6 +114,9 @@ const DefaultTemplate = (args, { argTypes }) => ({ :year="year" :dateOrder="dateOrder" :dateLabels="dateLabels" + :monthAsSelect="monthAsSelect" + :monthEmptyLabel="monthEmptyLabel" + :monthOptions="monthOptions" :name="name" :required="required" :error="error" @@ -188,6 +206,81 @@ CustomInputLabelsDateInput.args = { } CustomInputLabelsDateInput.storyName = 'Custom Input Labels' +export const MonthAsSelectDateInput = DefaultTemplate.bind({}) +MonthAsSelectDateInput.args = { + ...defaultProps, + label: 'Month as select form element', + monthAsSelect: true, +} +MonthAsSelectDateInput.storyName = 'Month as Select form element' + +export const CustomMonthSelectEmptyLabelDateInput = DefaultTemplate.bind({}) +CustomMonthSelectEmptyLabelDateInput.args = { + ...defaultProps, + label: 'Custom month empty label', + monthAsSelect: true, + monthEmptyLabel: 'Choose a month', +} +CustomMonthSelectEmptyLabelDateInput.storyName = 'Custom Month Empty Label' + +export const CustomMonthOptionsDateInput = DefaultTemplate.bind({}) +CustomMonthOptionsDateInput.args = { + ...defaultProps, + label: 'Custom month options', + monthAsSelect: true, + monthOptions: [ + { + value: 'January', + text: 'Jan - 1', + }, + { + value: 'February', + text: 'Feb - 2', + }, + { + value: 'March', + text: 'Mar - 3', + }, + { + value: 'April', + text: 'Apr - 4', + }, + { + value: 'May', + text: 'May - 5', + }, + { + value: 'June', + text: 'Jun - 6', + }, + { + value: 'July', + text: 'Jul - 7', + }, + { + value: 'August', + text: 'Aug - 8', + }, + { + value: 'September', + text: 'Sep - 9', + }, + { + value: 'October', + text: 'Oct - 10', + }, + { + value: 'November', + text: 'Nov - 11', + }, + { + value: 'December', + text: 'Dec - 12', + }, + ], +} +CustomMonthOptionsDateInput.storyName = 'Custom Month Options' + export const LabelSlotDateInput = DefaultTemplate.bind({}) LabelSlotDateInput.args = { ...defaultProps, diff --git a/src/components/UsaDateInput/UsaDateInput.test.js b/src/components/UsaDateInput/UsaDateInput.test.js index e628ed19..5e292929 100644 --- a/src/components/UsaDateInput/UsaDateInput.test.js +++ b/src/components/UsaDateInput/UsaDateInput.test.js @@ -380,4 +380,272 @@ describe('UsaDateInput', () => { cy.get('legend').should('not.exist') }) + + it('uses select element for month', () => { + mount(UsaDateInput, { + props: { + monthAsSelect: true, + id: 'custom-id', + }, + }).as('wrapper') + + cy.get('@wrapper') + .vue() + .then(vm => { + expect(vm.emitted()).to.not.have.property('update:month') + }) + + cy.get('.usa-form-group--month label') + .should('have.class', 'usa-label') + .and('have.attr', 'for', 'custom-id-date-month') + .and('have.contain', 'Month') + + cy.get('.usa-form-group--month select') + .as('select') + .should('have.id', 'custom-id-date-month') + .and('have.class', 'usa-select') + .and('have.attr', 'name', 'date_month') + .and('not.have.attr', 'aria-describedby', 'custom-id-hint') + + cy.get('@select') + .find('option:nth-of-type(1)') + .should('have.attr', 'value', '') + .and('contain', '- Select -') + + cy.get('@select') + .find('option:nth-of-type(2)') + .should('have.attr', 'value', '1') + .and('contain', '01 - January') + + cy.get('@select') + .find('option:nth-of-type(3)') + .should('have.attr', 'value', '2') + .and('contain', '02 - February') + + cy.get('@select') + .find('option:nth-of-type(4)') + .should('have.attr', 'value', '3') + .and('contain', '03 - March') + + cy.get('@select') + .find('option:nth-of-type(5)') + .should('have.attr', 'value', '4') + .and('contain', '04 - April') + + cy.get('@select') + .find('option:nth-of-type(6)') + .should('have.attr', 'value', '5') + .and('contain', '05 - May') + + cy.get('@select') + .find('option:nth-of-type(7)') + .should('have.attr', 'value', '6') + .and('contain', '06 - June') + + cy.get('@select') + .find('option:nth-of-type(8)') + .should('have.attr', 'value', '7') + .and('contain', '07 - July') + + cy.get('@select') + .find('option:nth-of-type(9)') + .should('have.attr', 'value', '8') + .and('contain', '08 - August') + + cy.get('@select') + .find('option:nth-of-type(10)') + .should('have.attr', 'value', '9') + .and('contain', '09 - September') + + cy.get('@select') + .find('option:nth-of-type(11)') + .should('have.attr', 'value', '10') + .and('contain', '10 - October') + + cy.get('@select') + .find('option:nth-of-type(12)') + .should('have.attr', 'value', '11') + .and('contain', '11 - November') + + cy.get('@select') + .find('option:nth-of-type(13)') + .should('have.attr', 'value', '12') + .and('contain', '12 - December') + + cy.get('@select').select('5') + + cy.get('@select').should('have.value', '5') + + cy.get('@wrapper') + .vue() + .then(vm => { + expect(vm.emitted()).to.have.property('update:month') + const currentMonthEvent = vm.emitted('update:month') + expect(currentMonthEvent).to.have.length(1) + expect(currentMonthEvent[currentMonthEvent.length - 1]).to.contain(5) + }) + }) + + it('uses custom month select options', () => { + mount(UsaDateInput, { + props: { + monthAsSelect: true, + id: 'custom-id', + required: true, + month: 'July', + monthEmptyLabel: 'Choose Month', + monthOptions: [ + { + value: 'January', + text: 'Jan - 1', + }, + { + value: 'February', + text: 'Feb - 2', + }, + { + value: 'March', + text: 'Mar - 3', + }, + { + value: 'April', + text: 'Apr - 4', + }, + { + value: 'May', + text: 'May - 5', + }, + { + value: 'June', + text: 'Jun - 6', + }, + { + value: 'July', + text: 'Jul - 7', + }, + { + value: 'August', + text: 'Aug - 8', + }, + { + value: 'September', + text: 'Sep - 9', + }, + { + value: 'October', + text: 'Oct - 10', + }, + { + value: 'November', + text: 'Nov - 11', + }, + { + value: 'December', + text: 'Dec - 12', + }, + ], + }, + slots: { + hint: () => 'Test select hint', + }, + }) + + cy.get('.usa-form-group--month label') + .should('have.class', 'usa-label') + .and('have.attr', 'for', 'custom-id-date-month') + .and('have.contain', 'Month') + + cy.get('.usa-form-group--month abbr') + .should('have.attr', 'title', 'required') + .and('contain', '*') + + cy.get('.usa-form-group--month select') + .as('select') + .should('have.id', 'custom-id-date-month') + .and('have.class', 'usa-select') + .and('have.attr', 'name', 'date_month') + .and('have.attr', 'aria-describedby', 'custom-id-hint') + .and('have.attr', 'required', 'required') + .and('have.value', 'July') + + cy.get('@select') + .find('option:nth-of-type(1)') + .should('have.attr', 'value', '') + .and('contain', 'Choose Month') + + cy.get('@select') + .find('option:nth-of-type(2)') + .should('have.attr', 'value', 'January') + .and('contain', 'Jan - 1') + + cy.get('@select') + .find('option:nth-of-type(3)') + .should('have.attr', 'value', 'February') + .and('contain', 'Feb - 2') + + cy.get('@select') + .find('option:nth-of-type(4)') + .should('have.attr', 'value', 'March') + .and('contain', 'Mar - 3') + + cy.get('@select') + .find('option:nth-of-type(5)') + .should('have.attr', 'value', 'April') + .and('contain', 'Apr - 4') + + cy.get('@select') + .find('option:nth-of-type(6)') + .should('have.attr', 'value', 'May') + .and('contain', 'May - 5') + + cy.get('@select') + .find('option:nth-of-type(7)') + .should('have.attr', 'value', 'June') + .and('contain', 'Jun - 6') + + cy.get('@select') + .find('option:nth-of-type(8)') + .should('have.attr', 'value', 'July') + .and('contain', 'Jul - 7') + + cy.get('@select') + .find('option:nth-of-type(9)') + .should('have.attr', 'value', 'August') + .and('contain', 'Aug - 8') + + cy.get('@select') + .find('option:nth-of-type(10)') + .should('have.attr', 'value', 'September') + .and('contain', 'Sep - 9') + + cy.get('@select') + .find('option:nth-of-type(11)') + .should('have.attr', 'value', 'October') + .and('contain', 'Oct - 10') + + cy.get('@select') + .find('option:nth-of-type(12)') + .should('have.attr', 'value', 'November') + .and('contain', 'Nov - 11') + + cy.get('@select') + .find('option:nth-of-type(13)') + .should('have.attr', 'value', 'December') + .and('contain', 'Dec - 12') + }) + + it('console prints deprecation warning if `monthAsSelect` is false', () => { + cy.stub(window.console, 'warn').as('consoleWarn') + + mount(UsaDateInput, { + props: { + monthAsSelect: false, + }, + }).as('wrapper') + + cy.get('@consoleWarn').should( + 'be.calledOnceWith', + `The 'monthAsSelect' prop is deprecated. Starting with vue-uswds 2.0 the month will always use a select form element. You can set the 'monthAsSelect' prop value to true to minimize changes.` + ) + }) }) diff --git a/src/components/UsaDateInput/UsaDateInput.vue b/src/components/UsaDateInput/UsaDateInput.vue index 05a5c77f..368d4325 100644 --- a/src/components/UsaDateInput/UsaDateInput.vue +++ b/src/components/UsaDateInput/UsaDateInput.vue @@ -2,6 +2,7 @@ import { computed, useSlots } from 'vue' import { nextId } from '@/utils/unique-id.js' import UsaTextInput from '@/components/UsaTextInput' +import UsaSelect from '@/components/UsaSelect' const slots = useSlots() const emit = defineEmits(['update:month', 'update:day', 'update:year']) @@ -31,6 +32,70 @@ const props = defineProps({ type: Object, default: () => ({ month: 'Month', day: 'Day', year: 'Year' }), }, + /** + * @deprecated + */ + monthAsSelect: { + type: Boolean, + default: false, + }, + monthEmptyLabel: { + type: String, + default: undefined, + }, + monthOptions: { + type: Array, + default: () => [ + { + value: 1, + text: '01 - January', + }, + { + value: 2, + text: '02 - February', + }, + { + value: 3, + text: '03 - March', + }, + { + value: 4, + text: '04 - April', + }, + { + value: 5, + text: '05 - May', + }, + { + value: 6, + text: '06 - June', + }, + { + value: 7, + text: '07 - July', + }, + { + value: 8, + text: '08 - August', + }, + { + value: 9, + text: '09 - September', + }, + { + value: 10, + text: '10 - October', + }, + { + value: 11, + text: '11 - November', + }, + { + value: 12, + text: '12 - December', + }, + ], + }, name: { type: String, default: 'date', @@ -49,6 +114,12 @@ const props = defineProps({ }, }) +if (!props.monthAsSelect) { + console.warn( + `The 'monthAsSelect' prop is deprecated. Starting with vue-uswds 2.0 the month will always use a select form element. You can set the 'monthAsSelect' prop value to true to minimize changes.` + ) +} + const computedId = computed(() => props.id || nextId('usa-date-input')) const computedErrorMessageId = computed( () => `${computedId.value}-error-message` @@ -102,20 +173,35 @@ const ariaDescribedby = computed(() => {