From fc52f5442993e37c6e66bd9daacabc7acb4ea351 Mon Sep 17 00:00:00 2001 From: Precious Onyenaucheya Date: Wed, 29 Jan 2025 16:57:49 +0000 Subject: [PATCH] add initial changes --- __snapshots__/layout/_template.spec.js.snap | 15 +++ src/components/button/_button.scss | 95 +++++++++++++++++++ src/components/header/_header.scss | 21 ++++ src/components/header/_macro-options.md | 54 +++++++---- src/components/header/_macro.njk | 66 +++++++++++++ src/components/header/_macro.spec.js | 66 +++++++++++++ src/components/header/_test-examples.js | 26 +++++ .../example-header-with-search-button.njk | 44 +++++++++ src/components/input/_input.scss | 8 ++ src/components/input/_macro.njk | 8 +- 10 files changed, 381 insertions(+), 22 deletions(-) create mode 100644 src/components/header/example-header-with-search-button.njk diff --git a/__snapshots__/layout/_template.spec.js.snap b/__snapshots__/layout/_template.spec.js.snap index bc80c256e6..3a6da27a6f 100644 --- a/__snapshots__/layout/_template.spec.js.snap +++ b/__snapshots__/layout/_template.spec.js.snap @@ -230,9 +230,12 @@ exports[`base page template matches the favicons block override snapshot 1`] = ` + + +
@@ -471,9 +474,12 @@ exports[`base page template matches the footer block override snapshot 1`] = `
+ + +
@@ -844,6 +850,7 @@ exports[`base page template matches the full configuration snapshot 1`] = `
+ @@ -885,6 +892,8 @@ exports[`base page template matches the full configuration snapshot 1`] = ` + +
@@ -1999,9 +2008,12 @@ exports[`base page template matches the meta block override snapshot 1`] = `
+ + +
@@ -2224,9 +2236,12 @@ exports[`base page template matches the social block override snapshot 1`] = `
+ + +
diff --git a/src/components/button/_button.scss b/src/components/button/_button.scss index 12addc8838..cb431cc0e5 100644 --- a/src/components/button/_button.scss +++ b/src/components/button/_button.scss @@ -34,6 +34,24 @@ $button-shadow-size: 3px; } } + &--header-search &, + &--header-search:active &, + &--header-search.active &, + &--header-search:active:focus &, + &--header-search.active:focus & { + &__inner { + border-radius: 0; + background: var(--ons-color-ocean-blue) !important; + box-shadow: 0 ems($button-shadow-size) 0 var(--ons-color-button-shadow) !important; + } + } + + &--header-search:focus & { + &__inner { + background: var(--ons-color-focus) !important; + } + } + &__inner { background: var(--ons-color-button); border-radius: $input-radius; @@ -153,6 +171,12 @@ $button-shadow-size: 3px; } } + &--header-search:hover & { + &__inner { + background: var(--ons-color-ocean-blue); + } + } + &--secondary:hover:not(&--disabled) & { &__inner { background: var(--ons-color-button-secondary-hover); @@ -550,6 +574,77 @@ $button-shadow-size: 3px; } } } + + &--search-icon { + align-items: center; + display: flex; + height: 67px; + padding: 0 1.5rem; + @include mq(l) { + height: 72px; + } + } + + &--search-icon & { + &__inner { + background: rgb(0 0 0 / 0%); + border: 0; + border-radius: 0; + box-shadow: none; + color: var(--ons-color-text-link); + font-weight: 700; + padding: 0; + .ons-icon { + fill: var(--ons-color-text-link); + height: 1rem; + margin-top: 0; + width: 1rem; + } + } + } + + &--search-icon:focus & { + &__inner { + background: none; + box-shadow: none; + } + } + + &--search-icon:focus:hover:not(:active, .active) { + .ons-btn__inner { + background: none; + } + } + + &--search-icon:active, + &--search-icon[aria-expanded='true'] { + background-color: var(--ons-color-branded-tint); + } + + &--search-icon:hover &, + &--search-icon:active &, + &--search-icon.active & { + &__inner { + background: none; + color: var(--ons-color-text-link-hover); + .ons-icon { + fill: var(--ons-color-text-link-hover); + } + } + } + + &--search-icon:active &, + &--search-icon:active:focus &, + &--search-icon.active &, + &--search-icon.active:focus & { + &__inner { + background: none; + color: var(--ons-color-text-link-hover); + .ons-icon { + fill: var(--ons-color-text-link-hover); + } + } + } } .ons-search__btn { diff --git a/src/components/header/_header.scss b/src/components/header/_header.scss index e41a2e169f..10c1e7c2bb 100644 --- a/src/components/header/_header.scss +++ b/src/components/header/_header.scss @@ -236,6 +236,27 @@ } } + &--basic & { + &__grid-top { + padding: 0; + } + } + + &-nav-search { + background-color: var(--ons-color-branded-tint); + margin-bottom: 1rem; + padding-top: 2.5rem; + padding-bottom: 2.5rem; + width: 100%; + &__key-list { + border-bottom: 1px solid var(--ons-color-ocean-blue); + margin-bottom: 2.5rem; + padding-bottom: 2.5rem; + padding-left: 0; + row-gap: 1rem; + } + } + .ons-btn { top: 0 !important; } diff --git a/src/components/header/_macro-options.md b/src/components/header/_macro-options.md index b1cf9c4c18..f93dca372e 100644 --- a/src/components/header/_macro-options.md +++ b/src/components/header/_macro-options.md @@ -1,22 +1,23 @@ -| Name | Type | Required | Description | -| --------------------- | ------------------------------------------------- | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -| phase | `PhaseBanner` [_(ref)_](/components/phase-banner) | false | Settings to set the Phase banner component within the HTML `
` element | -| wide | boolean | false | Set to “true” to increase the maximum width of the layout container to 1280px | -| fullWidth | boolean | false | Set to “true” to increase the maximum width of the layout container to the full width of the viewport | -| classes | string | false | Classes to add to the wrapping `header` | -| variants | array or string | false | An array of values or single value (string) to adjust the component using available variants: “internal”, "neutral", “description” and "basic" | -| mastheadLogoUrl | string | false | Wraps the masthead logo in a link. Set the URL for the HTML `href` attribute for the link. | -| mastheadLogo | object`` | false | Settings for a [custom organisation logo](#mastheadlogo) in the masthead. Defaults to the ONS logo. | -| language | object`` | false | Settings for the [language selector](#language) | -| serviceLinks | object`` | false | Settings for the [service links](#servicelinks) in the masthead | +| Name | Type | Required | Description | +| --------------------- | ------------------------------------------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| phase | `PhaseBanner` [_(ref)_](/components/phase-banner) | false | Settings to set the Phase banner component within the HTML `
` element | +| wide | boolean | false | Set to “true” to increase the maximum width of the layout container to 1280px | +| fullWidth | boolean | false | Set to “true” to increase the maximum width of the layout container to the full width of the viewport | +| classes | string | false | Classes to add to the wrapping `header` | +| variants | array or string | false | An array of values or single value (string) to adjust the component using available variants: “internal”, "neutral", “description” and "basic" | +| mastheadLogoUrl | string | false | Wraps the masthead logo in a link. Set the URL for the HTML `href` attribute for the link. | +| mastheadLogo | object`` | false | Settings for a [custom organisation logo](#mastheadlogo) in the masthead. Defaults to the ONS logo. | +| language | object`` | false | Settings for the [language selector](#language) | +| serviceLinks | object`` | false | Settings for the [service links](#servicelinks) in the masthead | | title | string | true (unless `titleLogo` is set or variant is set to basic) | The title for the service | -| description | string | false | Tagline or description for the service | -| titleAsH1 | boolean | false | Override to wrap the header `title` in an `

` heading | -| titleLogo | object`` | false | Settings for a [custom title logo](#titlelogo) in the header. | -| titleUrl | string | false | Wraps the title logo in a link. Set the URL for the HTML `href` attribute for the link. | -| button | object`` | false | Settings for the [sign out button](#signoutbutton) in the header used to exit a transactional service | -| navigation | array`` | false | Settings for the [main menu links](#navigation) | -| siteSearchAutosuggest | `Autosuggest` [_(ref)_](/components/autosuggest) | false | Sets the autosuggest functionality in the header | +| description | string | false | Tagline or description for the service | +| titleAsH1 | boolean | false | Override to wrap the header `title` in an `

` heading | +| titleLogo | object`` | false | Settings for a [custom title logo](#titlelogo) in the header. | +| titleUrl | string | false | Wraps the title logo in a link. Set the URL for the HTML `href` attribute for the link. | +| button | object`` | false | Settings for the [sign out button](#signoutbutton) in the header used to exit a transactional service | +| navigation | array`` | false | Settings for the [main menu links](#navigation) | +| siteSearchAutosuggest | `Autosuggest` [_(ref)_](/components/autosuggest) | false | Sets the autosuggest functionality in the header | +| searchLinks | object`` | false | Settings for the [search button navigation](#searchLinks) in the masthead | ## MastheadLogo @@ -61,6 +62,23 @@ | itemsList | array`` | true | Settings for an array of [list items](#item) for each navigation link | | toggleServicesButton | object`` | true | Settings for the [mobile service links toggle button](#togglebutton) | +## SearchLinks + +| Name | Type | Required | Description | +| ------------------ | ---------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| id | string | true | The HTML `id` of the `search button` element. Used for the `aria-controls` attribute for the search toggle button displayed on small viewports. | +| classes | string | false | Classes to add to the `search button` element | +| ariaLabel | string | false | The `aria-label` attribute added to the `search button` element. Defaults to Search navigation”. | +| popularSearches | array`` | true | Settings for an array of [popular searches](#searchitem) for each search link | +| toggleSearchButton | object`` | true | Settings for the [search toggle button](#toggleButton) | + +## SearchItem + +| Name | Type | Required | Description | +| ---- | ------ | -------- | ------------------------------------- | +| text | string | false | The text for the popular search item. | +| url | string | true | The URL for the popular search item | + ## Language | Name | Type | Required | Description | diff --git a/src/components/header/_macro.njk b/src/components/header/_macro.njk index d75d0981b4..2434338c63 100644 --- a/src/components/header/_macro.njk +++ b/src/components/header/_macro.njk @@ -180,6 +180,24 @@ {% endif %}

{% endif %} + {% if params.searchLinks %} + + {% endif %} {% if params.serviceLinks %} @@ -217,6 +235,54 @@ {% endif %} {% endif %} + + {% if params.searchLinks %} + + {% endif %} {% if params.variants != "basic" %}
diff --git a/src/components/header/_macro.spec.js b/src/components/header/_macro.spec.js index 0357aa40de..1e47cd4a75 100644 --- a/src/components/header/_macro.spec.js +++ b/src/components/header/_macro.spec.js @@ -9,6 +9,7 @@ import { mapAll } from '../../tests/helpers/cheerio'; import { EXAMPLE_HEADER_BASIC, EXAMPLE_SERVICE_LINKS_CONFIG, + EXAMPLE_HEADER_SEARCH_LINKS, EXAMPLE_HEADER_SERVICE_LINKS_MULTIPLE, EXAMPLE_HEADER_SERVICE_LINKS_SINGLE, EXAMPLE_HEADER_LANGUAGE_CONFIG, @@ -784,4 +785,69 @@ describe('FOR: Macro: Header', () => { }); }); }); + describe('GIVEN: Params: searchLinks', () => { + describe('WHEN: searchLinks are provided with a toggle search button', () => { + const $ = cheerio.load(renderComponent('header', EXAMPLE_HEADER_SEARCH_LINKS)); + + const faker = templateFaker(); + const buttonSpy = faker.spy('button', { suppressOutput: true }); + faker.renderComponent('header', EXAMPLE_HEADER_SEARCH_LINKS); + + test('THEN: renders search icon button on small screen', () => { + expect(buttonSpy.occurrences).toContainEqual({ + iconType: 'search', + classes: 'ons-u-fs-s--b ons-js-toggle-services', + type: 'button', + variants: 'search-icon', + attributes: { + 'aria-label': 'Toggle search', + 'aria-expanded': 'false', + }, + }); + }); + + test('THEN: renders search input form when button is clicked', () => { + expect($('.ons-header-nav-search').length).toBeGreaterThan(0); + }); + + test('THEN: renders the search field with full width', () => { + expect($('.ons-header-nav-search__key-list .ons-input').hasClass('ons-input--w-full')).toBe(true); + }); + }); + + describe('WHEN: popular searches are provided in searchLinks', () => { + const $ = cheerio.load(renderComponent('header', EXAMPLE_HEADER_SEARCH_LINKS)); + + test('THEN: renders popular searches list', () => { + const popularSearches = $('.ons-list--bare .ons-list__item').length; + expect(popularSearches).toBeGreaterThan(0); + }); + + test('THEN: renders correct links for popular searches', () => { + const popularSearchesLinks = mapAll($('.ons-list--bare .ons-list__item a'), (node) => node.attr('href')); + expect(popularSearchesLinks).toEqual(['#1', '#2', '#3']); + }); + + test('THEN: renders correct text for popular searches', () => { + const popularSearchesText = mapAll($('.ons-list--bare .ons-list__item a'), (node) => node.text().trim()); + expect(popularSearchesText).toEqual(['Popular Search 1', 'Popular Search 2', 'Popular Search 3']); + }); + }); + + describe('WHEN: searchLinks parameter is missing', () => { + const $ = cheerio.load(renderComponent('header', EXAMPLE_HEADER_BASIC)); + + test('THEN: does not render search icon button', () => { + expect($('.ons-js-toggle-services').length).toBe(0); + }); + + test('THEN: does not render search input form', () => { + expect($('.ons-header-nav-search').length).toBe(0); + }); + + test('THEN: does not render popular searches', () => { + expect($('.ons-list--bare').length).toBe(0); + }); + }); + }); }); diff --git a/src/components/header/_test-examples.js b/src/components/header/_test-examples.js index 634dd7c323..2d337a500b 100644 --- a/src/components/header/_test-examples.js +++ b/src/components/header/_test-examples.js @@ -150,3 +150,29 @@ export const EXAMPLE_HEADER_NAVIGATION_WITH_SITESEARCHAUTOSUGGEST = { language: 'en-gb', }, }; + +export const EXAMPLE_HEADER_SEARCH_LINKS = { + searchLinks: { + id: 'nav-links-external', + ariaLabel: 'Nav Search', + toggleSearchButton: { + text: 'Search', + ariaLabel: 'Toggle search', + }, + popularSearches: [ + { + url: '#1', + text: 'Popular Search 1', + }, + { + url: '#2', + text: 'Popular Search 2', + }, + { + url: '#3', + text: 'Popular Search 3', + external: true, + }, + ], + }, +}; diff --git a/src/components/header/example-header-with-search-button.njk b/src/components/header/example-header-with-search-button.njk new file mode 100644 index 0000000000..b3e7f01011 --- /dev/null +++ b/src/components/header/example-header-with-search-button.njk @@ -0,0 +1,44 @@ +--- +'fullWidth': true +--- + +{% from "components/header/_macro.njk" import onsHeader %} + +{{ + onsHeader({ + "variants": 'basic', + "searchLinks": + { + "id": "nav-links-external", + "ariaLabel": 'Nav Search', + "toggleSearchButton": { + "text": 'Search', + "ariaLabel": 'Toggle search' + }, + "popularSearches": [ + { + "url": '#1', + "text": 'Cost of living' + }, + { + "url": '#1', + "text": 'Inflation' + }, + { + "url": '#3', + "text": 'NHS waiting times', + "external": true + }, + { + "url": '#0', + "text": 'Wellbeing', + "external": true + },{ + "url": '#0', + "text": 'Baby names', + "external": true + } + ] + } + }) +}} diff --git a/src/components/input/_input.scss b/src/components/input/_input.scss index 5b43651889..9c763b1942 100644 --- a/src/components/input/_input.scss +++ b/src/components/input/_input.scss @@ -86,6 +86,10 @@ margin-top: 0.25rem; } +.ons-input__header-search { + border-radius: 0; +} + .ons-input--select { appearance: none; background: var(--ons-color-input-bg) @@ -149,6 +153,10 @@ height: 2.5rem; } +.ons-input_header-search-button { + gap: 0; +} + .ons-input-search { @extend .ons-input--block; @extend .ons-input--ghost; diff --git a/src/components/input/_macro.njk b/src/components/input/_macro.njk index cdc7a1ba7f..b8936cc43b 100644 --- a/src/components/input/_macro.njk +++ b/src/components/input/_macro.njk @@ -25,7 +25,7 @@ {% if params.accessiblePlaceholder %} {{ @@ -142,8 +142,8 @@ "html": buttonLabel, "text": params.searchButton.text, "id": params.searchButton.id, - "variants": 'small', - "classes": 'ons-search__btn' + (" " + params.searchButton.classes if params.searchButton.classes else ""), + "variants": 'header-search' if params.searchButton.variant == 'header' else 'small', + "classes": 'ons-search__btn' + (" " + params.searchButton.classes if params.searchButton.classes else "") + (" ons-search__header-btn" if params.searchButton.variant == 'header' else "") , "attributes": params.searchButton.attributes, "iconType": params.searchButton.iconType, "iconPosition": 'only' if params.searchButton.visuallyHideButtonText == true else 'before'