From c15e4214676d32716f4b3a098c1bb38e4ee38e5f Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Thu, 14 Mar 2024 08:06:55 +0000 Subject: [PATCH] Refactor date and duration components to work in jinja2 environments (#3080) --- README.md | 1 + ...nput-double-field_0_document_0_desktop.png | 3 + ...input-double-field_0_document_1_tablet.png | 3 + ...input-double-field_0_document_2_mobile.png | 3 + ...nput-single-field_0_document_0_desktop.png | 3 + ...input-single-field_0_document_1_tablet.png | 3 + ...input-single-field_0_document_2_mobile.png | 3 + ...tion-single-field_0_document_0_desktop.png | 3 + ...ation-single-field_0_document_1_tablet.png | 3 + ...ation-single-field_0_document_2_mobile.png | 3 + src/components/date-field-input/_macro.njk | 86 ----------- src/components/date-input/_macro.njk | 140 +++++++++--------- src/components/date-input/_macro.spec.js | 25 +++- .../example-date-input-double-field.njk | 27 ++++ .../example-date-input-single-field.njk | 18 +++ src/components/duration/_macro.njk | 100 +++++++------ src/components/duration/_macro.spec.js | 116 ++++++++++++++- .../example-duration-single-field.njk | 24 +++ src/components/metadata/_macro.njk | 20 +-- .../multiple-input-fields/_macro.njk | 49 ++++++ 20 files changed, 411 insertions(+), 222 deletions(-) create mode 100644 backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-double-field_0_document_0_desktop.png create mode 100644 backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-double-field_0_document_1_tablet.png create mode 100644 backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-double-field_0_document_2_mobile.png create mode 100644 backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-single-field_0_document_0_desktop.png create mode 100644 backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-single-field_0_document_1_tablet.png create mode 100644 backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-single-field_0_document_2_mobile.png create mode 100644 backstop_data/bitmaps_reference/ds-vr-test__components_duration_example-duration-single-field_0_document_0_desktop.png create mode 100644 backstop_data/bitmaps_reference/ds-vr-test__components_duration_example-duration-single-field_0_document_1_tablet.png create mode 100644 backstop_data/bitmaps_reference/ds-vr-test__components_duration_example-duration-single-field_0_document_2_mobile.png delete mode 100644 src/components/date-field-input/_macro.njk create mode 100644 src/components/date-input/example-date-input-double-field.njk create mode 100644 src/components/date-input/example-date-input-single-field.njk create mode 100644 src/components/duration/example-duration-single-field.njk create mode 100644 src/components/multiple-input-fields/_macro.njk diff --git a/README.md b/README.md index 64c9aa2c30..f9a8eb92af 100644 --- a/README.md +++ b/README.md @@ -158,3 +158,4 @@ yarn build - "render whitespace" set to "all" - "insert spaces when pressing tab" checked +- "trim trailing whitespace" checked diff --git a/backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-double-field_0_document_0_desktop.png b/backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-double-field_0_document_0_desktop.png new file mode 100644 index 0000000000..e797516193 --- /dev/null +++ b/backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-double-field_0_document_0_desktop.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:366c9aff9124200ace7d097a2227d594b42984ac5ebd47692a6126dcd4e231b2 +size 18957 diff --git a/backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-double-field_0_document_1_tablet.png b/backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-double-field_0_document_1_tablet.png new file mode 100644 index 0000000000..a46d9543a1 --- /dev/null +++ b/backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-double-field_0_document_1_tablet.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c95488d7659a0768dd81b0c0125abca78833f378e1418a76d063db2bb0d83208 +size 13842 diff --git a/backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-double-field_0_document_2_mobile.png b/backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-double-field_0_document_2_mobile.png new file mode 100644 index 0000000000..b1b4c7e3be --- /dev/null +++ b/backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-double-field_0_document_2_mobile.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e2f1d3d2f3d77101db234493308ed89060a298ce510e3e1cea26877c4e47ac9 +size 10587 diff --git a/backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-single-field_0_document_0_desktop.png b/backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-single-field_0_document_0_desktop.png new file mode 100644 index 0000000000..9db40cc6b4 --- /dev/null +++ b/backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-single-field_0_document_0_desktop.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:55948fafb4f0efcd5d807ecd6b4c926fadd1bd21b7bb3568157ca067c6dcf0d4 +size 15409 diff --git a/backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-single-field_0_document_1_tablet.png b/backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-single-field_0_document_1_tablet.png new file mode 100644 index 0000000000..ce0b2d4e69 --- /dev/null +++ b/backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-single-field_0_document_1_tablet.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb3d3f12ab40e4866c156f63f7bd02b4f61a01914472460bfe172562934075b2 +size 10290 diff --git a/backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-single-field_0_document_2_mobile.png b/backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-single-field_0_document_2_mobile.png new file mode 100644 index 0000000000..c04dc2134d --- /dev/null +++ b/backstop_data/bitmaps_reference/ds-vr-test__components_date-input_example-date-input-single-field_0_document_2_mobile.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ee6549c14f27303bb82cc63a8896548371b950c3ddc0b9b7cfa3e45835d43b72 +size 7231 diff --git a/backstop_data/bitmaps_reference/ds-vr-test__components_duration_example-duration-single-field_0_document_0_desktop.png b/backstop_data/bitmaps_reference/ds-vr-test__components_duration_example-duration-single-field_0_document_0_desktop.png new file mode 100644 index 0000000000..168c736c4c --- /dev/null +++ b/backstop_data/bitmaps_reference/ds-vr-test__components_duration_example-duration-single-field_0_document_0_desktop.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71378caad810dbb6135ad8418f26e9c92cba6f9c5a4e49d5ab66b3c746d148a7 +size 24525 diff --git a/backstop_data/bitmaps_reference/ds-vr-test__components_duration_example-duration-single-field_0_document_1_tablet.png b/backstop_data/bitmaps_reference/ds-vr-test__components_duration_example-duration-single-field_0_document_1_tablet.png new file mode 100644 index 0000000000..c37edbd254 --- /dev/null +++ b/backstop_data/bitmaps_reference/ds-vr-test__components_duration_example-duration-single-field_0_document_1_tablet.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:05c6bd046dd5fc0d58b621a65b79d0878e870f243c8f99902908cc50f393d5f5 +size 20703 diff --git a/backstop_data/bitmaps_reference/ds-vr-test__components_duration_example-duration-single-field_0_document_2_mobile.png b/backstop_data/bitmaps_reference/ds-vr-test__components_duration_example-duration-single-field_0_document_2_mobile.png new file mode 100644 index 0000000000..8554d76a96 --- /dev/null +++ b/backstop_data/bitmaps_reference/ds-vr-test__components_duration_example-duration-single-field_0_document_2_mobile.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b9b00c14d40f57790571900e616d8473c7e3f9c5e93e1e83066f9aee8576f960 +size 20778 diff --git a/src/components/date-field-input/_macro.njk b/src/components/date-field-input/_macro.njk deleted file mode 100644 index a53b43461d..0000000000 --- a/src/components/date-field-input/_macro.njk +++ /dev/null @@ -1,86 +0,0 @@ -{% from "components/mutually-exclusive/_macro.njk" import onsMutuallyExclusive %} -{% from "components/fieldset/_macro.njk" import onsFieldset %} -{% from "components/input/_macro.njk" import onsInput %} -{% from "components/error/_macro.njk" import onsError %} - -{% macro onsDateFieldInput(params) %} - {% set fields %} - {% for field in params.fields %} - {{ - onsInput({ - "id": field.id, - "classes": field.classes, - "width": field.width, - "type": "number", - "name": field.name, - "value": field.value, - "min": field.min, - "max": field.max, - "maxLength": field.maxLength, - "suffix": field.suffix, - "label": field.label, - "attributes": field.attributes, - "error": field.error, - "fieldId": field.fieldId - }) - }} - {% endfor %} - {% endset %} - - {% if numberOfFields > 1 %} - {% set fields %} -
- {{ fields | safe }} -
- {% endset %} - {% endif %} - - {% if params.mutuallyExclusive %} - {% set mutuallyExclusive = params.mutuallyExclusive | setAttributes({ - "id": params.id, - "legend": params.legendOrLabel, - "legendClasses": params.legendClasses, - "description": params.description, - "error": params.error, - "classes": params.classes, - "legendIsQuestionTitle": params.legendIsQuestionTitle, - "dontWrap": params.dontWrap, - "exclusiveOptions": params.mutuallyExclusive.exclusiveOptions, - "or": params.mutuallyExclusive.or, - "deselectMessage": params.mutuallyExclusive.deselectMessage, - "deselectGroupAdjective": params.mutuallyExclusive.deselectGroupAdjective, - "deselectExclusiveOptionAdjective": params.mutuallyExclusive.deselectExclusiveOptionAdjective - }) %} - - {% call onsMutuallyExclusive(mutuallyExclusive) %} -
- {{ fields | safe }} -
- {% endcall %} - {% elif params.numberOfFields > 1 %} - {% call onsFieldset({ - "id": params.id, - "legend": params.legendOrLabel, - "classes": params.classes, - "description": params.description, - "legendClasses": params.legendClasses, - "error": params.error, - "legendIsQuestionTitle": params.legendIsQuestionTitle, - "dontWrap": params.dontWrap - }) %} -
- {{ fields | safe }} -
- {% endcall %} - {% elif params.numberOfFields == 1 %} - {% if params.error %} - {% call onsError(params.error) %} - {{ fields | safe }} - {% endcall %} - {% else %} - {{ fields | safe }} - {% endif %} - {% else %} - {{ fields | safe }} - {% endif %} -{% endmacro %} diff --git a/src/components/date-input/_macro.njk b/src/components/date-input/_macro.njk index 3f354844e4..a0f700d33f 100644 --- a/src/components/date-input/_macro.njk +++ b/src/components/date-input/_macro.njk @@ -1,8 +1,9 @@ -{% from "components/date-field-input/_macro.njk" import onsDateFieldInput %} +{% from "components/multiple-input-fields/_macro.njk" import onsMultipleInputFields %} +{% from "components/input/_macro.njk" import onsInput %} {% macro onsDateInput(params) %} {% set exclusiveClass = " ons-js-exclusive-group-item" if params.mutuallyExclusive else "" %} - {%- set numberOfFields = 0 -%} + {%- set numberOfFields = 0 -%} {%- if params.day -%} {%- set numberOfFields = numberOfFields + 1 -%} @@ -16,82 +17,83 @@ {%- set numberOfFields = numberOfFields + 1 -%} {%- endif -%} - {% set fields = [] %} - {% if params.day %} - {% set dayField = { - "id": params.id + "-day", - "label": { - "text": params.day.label.text if numberOfFields > 1 else params.legendOrLabel, - "description": params.day.label.description if numberOfFields > 1 else params.description, - "id": params.id + "-day-label" - }, - "name": params.day.name, - "width": "2", - "min": 1, - "max": 31, - "maxLength": 2, - "attributes": params.day.attributes, - "value": params.day.value, - "classes": (" ons-input--error" if (params.error and params.day.error) or (params.error and not params.day.error and not params.month.error and not params.year.error) else "") + exclusiveClass | default("") - } %} - {% set fields = (fields.push(dayField), fields) %} - {% endif %} - {% if params.month %} - {% set monthField = { - "id": params.id + "-month", - "label": { - "text": params.month.label.text if numberOfFields > 1 else params.legendOrLabel, - "description": params.month.label.description if numberOfFields > 1 else params.description, - "id": params.id + "-month-label" - }, - "name": params.month.name, - "width": "2", - "min": 1, - "max": 12, - "maxLength": 2, - "attributes": params.month.attributes, - "value": params.month.value, - "classes": (" ons-input--error" if (params.error and params.month.error) or (params.error and not params.day.error and not params.month.error and not params.year.error) else "") + exclusiveClass | default("") - } %} - {% set fields = (fields.push(monthField), fields) %} - {% endif %} - {% if params.year %} - {% set yearField = { - "id": params.id + "-year", - "label": { - "text": params.year.label.text if numberOfFields > 1 else params.legendOrLabel, - "description": params.year.label.description if numberOfFields > 1 else params.description, - "id": params.id + "-year-label" - }, - "name": params.year.name, - "width": "4", - "min": 1000, - "max": 3000, - "maxLength": 4, - "attributes": params.year.attributes, - "value": params.year.value, - "classes": (" ons-input--error" if (params.error and params.year.error) or (params.error and not params.day.error and not params.month.error and not params.year.error) else "") + exclusiveClass | default("") - } %} - {% set fields = (fields.push(yearField), fields) %} - {% endif %} + {% set fields %} + {% if params.day %} + {{ onsInput({ + "id": params.id + "-day", + "type": "number", + "name": params.day.name, + "classes": (" ons-input--error" if (params.error and params.day.error) or (params.error and not params.day.error and not params.month.error and not params.year.error) else "") + exclusiveClass | default(""), + "width": "2", + "min": 1, + "max": 31, + "maxLength": 2, + "attributes": params.day.attributes, + "label": { + "text": params.day.label.text if numberOfFields > 1 else params.legendOrLabel, + "description": params.day.label.description if numberOfFields > 1 else params.description, + "id": params.id + "-day-label" + }, + "value": params.day.value, + "error": params.error if numberOfFields < 2 + }) }} + {% endif %} - {{ onsDateFieldInput({ + {% if params.month %} + {{ onsInput({ + "id": params.id + "-month", + "type": "number", + "name": params.month.name, + "classes": (" ons-input--error" if (params.error and params.month.error) or (params.error and not params.day.error and not params.month.error and not params.year.error) else "") + exclusiveClass | default(""), + "width": "2", + "min": 1, + "max": 12, + "maxLength": 2, + "attributes": params.month.attributes, + "label": { + "text": params.month.label.text if numberOfFields > 1 else params.legendOrLabel, + "description": params.month.label.description if numberOfFields > 1 else params.description, + "id": params.id + "-month-label" + }, + "value": params.month.value, + "error": params.error if numberOfFields < 2 + }) }} + {% endif %} + + {% if params.year %} + {{ onsInput({ + "id": params.id + "-year", + "type": "number", + "name": params.year.name, + "classes": (" ons-input--error" if (params.error and params.year.error) or (params.error and not params.day.error and not params.month.error and not params.year.error) else "") + exclusiveClass | default(""), + "width": "4", + "min": 1000, + "max": 3000, + "maxLength": 4, + "attributes": params.year.attributes, + "label": { + "text": params.year.label.text if numberOfFields > 1 else params.legendOrLabel, + "description": params.year.label.description if numberOfFields > 1 else params.description, + "id": params.id + "-year-label" + }, + "value": params.year.value, + "error": params.error if numberOfFields < 2 + }) }} + {% endif %} + {% endset %} + + {{ onsMultipleInputFields({ "id": params.id, "legendOrLabel": params.legendOrLabel, "description": params.description, "mutuallyExclusive": params.mutuallyExclusive, - "fields": fields, "numberOfFields": numberOfFields, + "fields": fields, "error": params.error, "legend": params.legendOrLabel, "dontWrap": params.dontWrap, - "legendClasses": params.legendClasses, - "legendIsQuestionTitle": params.legendIsQuestionTitle, "classes": params.classes, - "exclusiveOptions": params.mutuallyExclusive.exclusiveOptions, - "or": params.mutuallyExclusive.or, - "deselectMessage": params.mutuallyExclusive.deselectMessage, - "deselectGroupAdjective": params.mutuallyExclusive.deselectGroupAdjective, - "deselectExclusiveOptionAdjective": params.mutuallyExclusive.deselectExclusiveOptionAdjective + "legendClasses": params.legendClasses, + "legendIsQuestionTitle": params.legendIsQuestionTitle }) }} {% endmacro %} diff --git a/src/components/date-input/_macro.spec.js b/src/components/date-input/_macro.spec.js index 7c5e99c1cc..e4a8b7ac42 100644 --- a/src/components/date-input/_macro.spec.js +++ b/src/components/date-input/_macro.spec.js @@ -107,7 +107,7 @@ const EXAMPLE_YEAR_FIELD_WITH_ERROR = { }, }; -const EXAMPLE_YEAR_FIELD_WITH_NO_ERROR = { +const EXAMPLE_YEAR_FIELD_WITH_ERROR_FALSE = { year: { label: { text: 'Year', @@ -148,9 +148,9 @@ const EXAMPLE_DATE_MULTIPLE_FIELDS_WITH_ERROR = { ...EXAMPLE_YEAR_FIELD_WITH_ERROR, }; -const EXAMPLE_DATE_MULTIPLE_FIELDS_WITH_NO_ERROR = { +const EXAMPLE_DATE_SINGLE_FIELD_WITH_ERROR_FALSE = { ...EXAMPLE_DATE_INPUT_BASE, - ...EXAMPLE_YEAR_FIELD_WITH_NO_ERROR, + ...EXAMPLE_YEAR_FIELD_WITH_ERROR_FALSE, }; describe('macro: date input', () => { @@ -177,6 +177,7 @@ describe('macro: date input', () => { max: 31, maxLength: 2, classes: '', + error: '', label: { text: 'Day', description: 'The day', @@ -204,6 +205,7 @@ describe('macro: date input', () => { max: 12, maxLength: 2, classes: '', + error: '', label: { text: 'Month', description: 'The month', @@ -231,6 +233,7 @@ describe('macro: date input', () => { max: 3000, maxLength: 4, classes: '', + error: '', label: { text: 'Year', description: 'The year', @@ -249,6 +252,12 @@ describe('macro: date input', () => { expect($(div).hasClass('ons-field-group')).toBe(true); }); + it('has the correct number of inputs', () => { + const $ = cheerio.load(renderComponent('date-input', EXAMPLE_DATE_MULTIPLE_FIELDS)); + const $inputs = $('.ons-input'); + expect($inputs.length).toBe(3); + }); + it('has the expected `fieldset` output', () => { const faker = templateFaker(); const fieldsetSpy = faker.spy('fieldset'); @@ -356,6 +365,12 @@ describe('macro: date input', () => { }); }); + it('has the correct number of inputs', () => { + const $ = cheerio.load(renderComponent('date-input', EXAMPLE_DATE_SINGLE_FIELD)); + const $inputs = $('.ons-input'); + expect($inputs.length).toBe(1); + }); + it('has the expected `error` output', () => { const faker = templateFaker(); const errorSpy = faker.spy('error'); @@ -371,7 +386,7 @@ describe('macro: date input', () => { }); }); - describe('mode: multiple fields with single field error', () => { + describe('mode: multiple fields with errors', () => { it('passes jest-axe checks', async () => { const $ = cheerio.load(renderComponent('date-input', EXAMPLE_DATE_MULTIPLE_FIELDS_WITH_SINGLE_ERROR)); @@ -394,7 +409,7 @@ describe('macro: date input', () => { }); it('does not provide error class when error parameter set to false', async () => { - const $ = cheerio.load(renderComponent('date-input', EXAMPLE_DATE_MULTIPLE_FIELDS_WITH_NO_ERROR)); + const $ = cheerio.load(renderComponent('date-input', EXAMPLE_DATE_SINGLE_FIELD_WITH_ERROR_FALSE)); const $errorContent = $('.ons-input--error'); expect($errorContent.length).toBe(0); diff --git a/src/components/date-input/example-date-input-double-field.njk b/src/components/date-input/example-date-input-double-field.njk new file mode 100644 index 0000000000..28019c475c --- /dev/null +++ b/src/components/date-input/example-date-input-double-field.njk @@ -0,0 +1,27 @@ +{% from "components/date-input/_macro.njk" import onsDateInput %} + +{{ + onsDateInput({ + "id": "date-input-example-double", + "legendOrLabel": "Month and year of last MOT", + "description": "For example, 3 2023", + "month": { + "label": { + "text": "Month" + }, + "name": "month", + "attributes": { + "autocomplete": "mot-month" + } + }, + "year": { + "label": { + "text": "Year" + }, + "name": "year", + "attributes": { + "autocomplete": "mot-year" + } + } + }) +}} diff --git a/src/components/date-input/example-date-input-single-field.njk b/src/components/date-input/example-date-input-single-field.njk new file mode 100644 index 0000000000..39bc0d9eb8 --- /dev/null +++ b/src/components/date-input/example-date-input-single-field.njk @@ -0,0 +1,18 @@ +{% from "components/date-input/_macro.njk" import onsDateInput %} + +{{ + onsDateInput({ + "id": "date-input-example-single", + "legendOrLabel": "Year of birth", + "description": "For example, 1980", + "year": { + "label": { + "text": "Year" + }, + "name": "year", + "attributes": { + "autocomplete": "bday-year" + } + } + }) +}} diff --git a/src/components/duration/_macro.njk b/src/components/duration/_macro.njk index 871e5d11cc..7d04d6ae2d 100644 --- a/src/components/duration/_macro.njk +++ b/src/components/duration/_macro.njk @@ -1,7 +1,10 @@ -{% from "components/date-field-input/_macro.njk" import onsDateFieldInput %} +{% from "components/multiple-input-fields/_macro.njk" import onsMultipleInputFields %} +{% from "components/input/_macro.njk" import onsInput %} {% macro onsDuration(params) %} + {% set exclusiveClass = " ons-js-exclusive-group-item" if params.mutuallyExclusive else "" %} {% set numberOfFields = 0 %} + {% if params.field1 %} {% set numberOfFields = numberOfFields + 1 %} {% endif %} @@ -10,54 +13,55 @@ {% set numberOfFields = numberOfFields + 1 %} {% endif %} - {% set fields = [] %} - {% if params.field1 %} - {% set field1 = { - "id": params.field1.id, - "name": params.field1.name, - "attributes": params.field1.attributes, - "label": { - "text": params.legendOrLabel if numberOfFields < 2, - "description": params.description if numberOfFields < 2 - }, - "suffix": { - "text": params.field1.suffix.text, - "title": params.field1.suffix.title, - "id": params.field1.suffix.id - }, - "width": "2", - "fieldId": params.id if numberOfFields < 2, - "value": params.field1.value, - "error": params.error if numberOfFields < 2, - "classes": (" ons-input--error" if (params.error and params.field1.error) or (params.error and not params.field1.error and not params.field2.error) else "") + (" ons-js-exclusive-group-item" if params.mutuallyExclusive else "") - } %} - {% set fields = (fields.push(field1), fields) %} - {% endif %} + {% set fields %} + {% if params.field1 %} + {{ onsInput({ + "id": params.field1.id, + "type": "number", + "name": params.field1.name, + "classes": (" ons-input--error" if (params.error and params.field1.error) or (numberOfFields > 1 and params.error and not params.field1.error and not params.field2.error) else "") + exclusiveClass | default(""), + "width": "2", + "attributes": params.field1.attributes, + "value": params.field1.value, + "suffix": { + "text": params.field1.suffix.text, + "title": params.field1.suffix.title, + "id": params.field1.suffix.id + }, + "label": { + "text": params.legendOrLabel if numberOfFields < 2, + "description": params.description if numberOfFields < 2 + }, + "fieldId": params.id if numberOfFields < 2, + "error": params.error if numberOfFields < 2 + }) }} + {% endif %} - {% if params.field2 %} - {% set field2 = { - "id": params.field2.id, - "name": params.field2.name, - "attributes": params.field2.attributes, - "width": "2", - "label": { - "text": params.legendOrLabel if numberOfFields < 2, - "description": params.description if numberOfFields < 2 - }, - "suffix": { - "text": params.field2.suffix.text, - "title": params.field2.suffix.title, - "id": params.field2.suffix.id - }, - "value": params.field2.value, - "error": params.error if numberOfFields < 2, - "fieldId": params.id if numberOfFields < 2, - "classes": (" ons-input--error" if (params.error and params.field2.error) or (params.error and not params.field1.error and not params.field2.error) else "") + (" ons-js-exclusive-group-item" if params.mutuallyExclusive else "") - } %} - {% set fields = (fields.push(field2), fields) %} - {% endif %} - - {{ onsDateFieldInput({ + {% if params.field2 %} + {{ onsInput({ + "id": params.field2.id, + "type": "number", + "name": params.field2.name, + "classes": (" ons-input--error" if (params.error and params.field2.error) or (numberOfFields > 1 and params.error and not params.field1.error and not params.field2.error) else "") + exclusiveClass | default(""), + "width": "2", + "attributes": params.field2.attributes, + "value": params.field2.value, + "suffix": { + "text": params.field2.suffix.text, + "title": params.field2.suffix.title, + "id": params.field2.suffix.id + }, + "label": { + "text": params.legendOrLabel if numberOfFields < 2, + "description": params.description if numberOfFields < 2 + }, + "fieldId": params.id if numberOfFields < 2, + "error": params.error if numberOfFields < 2 + }) }} + {% endif %} + {% endset %} + + {{ onsMultipleInputFields({ "id": params.id, "legendOrLabel": params.legendOrLabel, "description": params.description, diff --git a/src/components/duration/_macro.spec.js b/src/components/duration/_macro.spec.js index 25eb9cff42..d4bf88c1cb 100644 --- a/src/components/duration/_macro.spec.js +++ b/src/components/duration/_macro.spec.js @@ -11,6 +11,15 @@ const EXAMPLE_DURATION_INPUT_BASE = { description: 'Enter “0” into the years field if you have lived at this address for less than a year', }; +const EXAMPLE_DURATION_INPUT_BASE_WITH_ERROR = { + id: 'duration', + legendOrLabel: 'How long have you lived at this address?', + description: 'Enter “0” into the years field if you have lived at this address for less than a year', + error: { + text: 'Enter how long you have lived at this address', + }, +}; + const EXAMPLE_FIELD_1 = { field1: { id: 'address-duration-years', @@ -35,6 +44,45 @@ const EXAMPLE_FIELD_2 = { }, }; +const EXAMPLE_FIELD_1_WITH_ERROR = { + field1: { + id: 'address-duration-years', + name: 'address-duration-years', + value: '0', + error: true, + suffix: { + text: 'Years', + id: 'address-duration-years-suffix', + }, + }, +}; + +const EXAMPLE_FIELD_2_WITH_ERROR = { + field2: { + id: 'address-duration-months', + name: 'address-duration-months', + value: '0', + error: true, + suffix: { + text: 'Months', + id: 'address-duration-months-suffix', + }, + }, +}; + +const EXAMPLE_FIELD_1_WITH_ERROR_FALSE = { + field1: { + id: 'address-duration-years', + name: 'address-duration-years', + value: '0', + error: false, + suffix: { + text: 'Years', + id: 'address-duration-years-suffix', + }, + }, +}; + const EXAMPLE_DURATION_SINGLE_FIELD = { ...EXAMPLE_DURATION_INPUT_BASE, ...EXAMPLE_FIELD_1, @@ -46,6 +94,23 @@ const EXAMPLE_DURATION_MULTIPLE_FIELDS = { ...EXAMPLE_FIELD_2, }; +const EXAMPLE_DURATION_MULTIPLE_FIELDS_WITH_SINGLE_ERROR = { + ...EXAMPLE_DURATION_INPUT_BASE_WITH_ERROR, + ...EXAMPLE_FIELD_1, + ...EXAMPLE_FIELD_2_WITH_ERROR, +}; + +const EXAMPLE_DURATION_MULTIPLE_FIELDS_WITH_ERROR = { + ...EXAMPLE_DURATION_INPUT_BASE_WITH_ERROR, + ...EXAMPLE_FIELD_1_WITH_ERROR, + ...EXAMPLE_FIELD_2_WITH_ERROR, +}; + +const EXAMPLE_DURATION_MULTIPLE_FIELDS_WITH_ERROR_FALSE = { + ...EXAMPLE_DURATION_INPUT_BASE, + ...EXAMPLE_FIELD_1_WITH_ERROR_FALSE, +}; + describe('macro: duration', () => { describe('mode: multiple fields', () => { it('passes jest-axe checks', async () => { @@ -109,6 +174,12 @@ describe('macro: duration', () => { expect($(div).hasClass('ons-field-group')).toBe(true); }); + it('has the correct number of inputs', () => { + const $ = cheerio.load(renderComponent('duration', EXAMPLE_DURATION_MULTIPLE_FIELDS)); + const $inputs = $('.ons-input'); + expect($inputs.length).toBe(2); + }); + it('has the expected `fieldset` output', () => { const faker = templateFaker(); const fieldsetSpy = faker.spy('fieldset'); @@ -141,10 +212,11 @@ describe('macro: duration', () => { mutuallyExclusive: { legendClasses: 'custom-legend-class', dontWrap: true, + classes: undefined, legendIsQuestionTitle: true, error: false, mutuallyExclusive: { - checkbox: {}, + exclusiveOptions: {}, or: 'Or', deselectMessage: 'Deselect message', deselectGroupAdjective: 'Deselect group adjective', @@ -166,10 +238,10 @@ describe('macro: duration', () => { ...EXAMPLE_DURATION_MULTIPLE_FIELDS, legendClasses: 'custom-legend-class', dontWrap: true, + classes: undefined, legendIsQuestionTitle: true, error: false, mutuallyExclusive: { - checkbox: {}, or: 'Or', deselectMessage: 'Deselect message', deselectGroupAdjective: 'Deselect group adjective', @@ -183,9 +255,9 @@ describe('macro: duration', () => { description: 'Enter “0” into the years field if you have lived at this address for less than a year', legendClasses: 'custom-legend-class', dontWrap: true, + classes: undefined, legendIsQuestionTitle: true, error: false, - checkbox: {}, or: 'Or', deselectMessage: 'Deselect message', deselectGroupAdjective: 'Deselect group adjective', @@ -215,7 +287,6 @@ describe('macro: duration', () => { classes: '', value: '0', attributes: undefined, - classes: '', error: undefined, fieldId: 'duration', label: { @@ -226,10 +297,17 @@ describe('macro: duration', () => { suffix: { id: 'address-duration-years-suffix', text: 'Years', + title: undefined, }, }); }); + it('has the correct number of inputs', () => { + const $ = cheerio.load(renderComponent('duration', EXAMPLE_DURATION_SINGLE_FIELD)); + const $inputs = $('.ons-input'); + expect($inputs.length).toBe(1); + }); + it('has the expected `error` output', () => { const faker = templateFaker(); const errorSpy = faker.spy('error'); @@ -244,4 +322,34 @@ describe('macro: duration', () => { }); }); }); + + describe('mode: multiple fields with errors', () => { + it('passes jest-axe checks', async () => { + const $ = cheerio.load(renderComponent('duration', EXAMPLE_DURATION_MULTIPLE_FIELDS_WITH_SINGLE_ERROR)); + + const results = await axe($.html()); + expect(results).toHaveNoViolations(); + }); + + it('has the provided error class on one input', async () => { + const $ = cheerio.load(renderComponent('duration', EXAMPLE_DURATION_MULTIPLE_FIELDS_WITH_SINGLE_ERROR)); + const $errorContent = $('.ons-input--error'); + + expect($errorContent.length).toBe(1); + }); + + it('has the provided error class on multiple inputs', async () => { + const $ = cheerio.load(renderComponent('duration', EXAMPLE_DURATION_MULTIPLE_FIELDS_WITH_ERROR)); + const $errorContent = $('.ons-input--error'); + + expect($errorContent.length).toBe(2); + }); + + it('does not provide error class when error parameter set to false', async () => { + const $ = cheerio.load(renderComponent('duration', EXAMPLE_DURATION_MULTIPLE_FIELDS_WITH_ERROR_FALSE)); + const $errorContent = $('.ons-input--error'); + + expect($errorContent.length).toBe(0); + }); + }); }); diff --git a/src/components/duration/example-duration-single-field.njk b/src/components/duration/example-duration-single-field.njk new file mode 100644 index 0000000000..001b340e9a --- /dev/null +++ b/src/components/duration/example-duration-single-field.njk @@ -0,0 +1,24 @@ +{% from "components/question/_macro.njk" import onsQuestion %} +{% from "components/duration/_macro.njk" import onsDuration %} + +{% call onsQuestion({ + "title": "How many years have you lived at this address?", + "description": "

Enter “0” into the years field if you have lived at this address for less than a year

", + "legendIsQuestionTitle": true +}) %} + {{ onsDuration({ + "id": "address-duration-example-single", + "dontWrap": true, + "field1": { + "id": "address-duration-years-example", + "name": "address-duration-years", + "suffix": { + "text": "Years", + "id": "address-duration-years-suffix-example" + }, + "attributes": { + "autocomplete": "off" + } + } + }) }} +{% endcall %} diff --git a/src/components/metadata/_macro.njk b/src/components/metadata/_macro.njk index ea4a0f2c0a..5f11c923ac 100644 --- a/src/components/metadata/_macro.njk +++ b/src/components/metadata/_macro.njk @@ -1,14 +1,14 @@ {% from "components/description-list/_macro.njk" import onsDescriptionList %} {% macro onsMetadata(params) %} -{{ - onsDescriptionList({ - "id": params.id, - "classes": params.classes, - "descriptionListLabel": params.metadataLabel, - "termCol": params.termCol, - "descriptionCol": params.descriptionCol, - "itemsList": params.itemsList - }) -}} + {{ + onsDescriptionList({ + "id": params.id, + "classes": params.classes, + "descriptionListLabel": params.metadataLabel, + "termCol": params.termCol, + "descriptionCol": params.descriptionCol, + "itemsList": params.itemsList + }) + }} {% endmacro %} diff --git a/src/components/multiple-input-fields/_macro.njk b/src/components/multiple-input-fields/_macro.njk new file mode 100644 index 0000000000..aeae347571 --- /dev/null +++ b/src/components/multiple-input-fields/_macro.njk @@ -0,0 +1,49 @@ +{% from "components/mutually-exclusive/_macro.njk" import onsMutuallyExclusive %} +{% from "components/fieldset/_macro.njk" import onsFieldset %} + +{% macro onsMultipleInputFields(params) %} + {% if params.numberOfFields > 1 %} + {% set fields %} +
+ {{ params.fields | safe }} +
+ {% endset %} + {% else %} + {{ params.fields | safe }} + {% endif %} + + {% if params.mutuallyExclusive %} + {% call onsMutuallyExclusive({ + "id": params.id, + "legend": params.legendOrLabel, + "legendClasses": params.legendClasses, + "description": params.description, + "classes": params.classes, + "dontWrap": params.dontWrap if params.numberOfFields > 1 else true, + "legendIsQuestionTitle": params.legendIsQuestionTitle, + "exclusiveOptions": params.mutuallyExclusive.exclusiveOptions, + "or": params.mutuallyExclusive.or, + "deselectMessage": params.mutuallyExclusive.deselectMessage, + "deselectGroupAdjective": params.mutuallyExclusive.deselectGroupAdjective, + "deselectExclusiveOptionAdjective": params.mutuallyExclusive.deselectExclusiveOptionAdjective, + "error": params.error + }) %} + {{ fields | safe }} + {% endcall %} + {% elif params.numberOfFields > 1 %} + {% call onsFieldset({ + "id": params.id, + "legend": params.legendOrLabel, + "legendClasses": params.legendClasses, + "description": params.description, + "classes": params.classes, + "dontWrap": params.dontWrap, + "legendIsQuestionTitle": params.legendIsQuestionTitle, + "error": params.error + }) %} + {{ fields | safe }} + {% endcall %} + {% else %} + {{ fields | safe }} + {% endif %} +{% endmacro %}