@financial-times/dotcom-ui-header 8.1.0 → 9.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/browser.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import Header from '@financial-times/o-header'
2
2
  import TopicSearch from 'n-topic-search'
3
-
3
+ import EnhancedSearch from './src/enhanced-search/enhancedSearch'
4
4
  /**
5
5
  * @typedef HeaderOptions
6
6
  * @property { HTMLElement } [rootElement] - the root element passed to o-header
7
7
  * @property { string } [hostName]
8
+ * @property { string } [enhancedSearchUrl]
8
9
  */
9
10
 
10
11
  /**
@@ -16,7 +17,9 @@ export const init = (headerOptions = {}) => {
16
17
  '.o-header [data-n-topic-search], .o-header__drawer [data-n-topic-search]'
17
18
  )
18
19
  topicSearchElements.forEach((element) => {
19
- new TopicSearch(element, headerOptions)
20
+ headerOptions.enhancedSearchUrl
21
+ ? new EnhancedSearch(element, headerOptions)
22
+ : new TopicSearch(element, headerOptions)
20
23
  })
21
24
 
22
25
  Header.init(headerOptions.rootElement)
@@ -222,7 +222,7 @@
222
222
  "affectsGlobalScope": false
223
223
  },
224
224
  "../src/index.tsx": {
225
- "version": "f076cad62acf3ca2bca2348a445aeef886e7c0db4ae610eafba754942df1fbec",
225
+ "version": "a8079d2415c7f843d4b61f63b855b5b7f2bada6d6952ff1af818e4e22cc9aa4d",
226
226
  "signature": "193a1c1b82fe624c251dd16b488b6b90a155e072da0ace6b843ec85f99df0c6e",
227
227
  "affectsGlobalScope": false
228
228
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/dotcom-ui-header",
3
- "version": "8.1.0",
3
+ "version": "9.0.0-beta.2",
4
4
  "description": "",
5
5
  "browser": "browser.js",
6
6
  "main": "component.js",
@@ -22,7 +22,7 @@
22
22
  "author": "",
23
23
  "license": "MIT",
24
24
  "dependencies": {
25
- "@financial-times/dotcom-types-navigation": "^8.1.0",
25
+ "@financial-times/dotcom-types-navigation": "^9.0.0-beta.2",
26
26
  "n-topic-search": "^4.0.0",
27
27
  "n-ui-foundations": "^9.0.0"
28
28
  },
@@ -0,0 +1,135 @@
1
+ import BaseRenderer from 'n-topic-search/src/renderers/base-renderer'
2
+
3
+ const DISPLAY_ITEMS = 5
4
+
5
+ class CustomSuggestionList extends BaseRenderer {
6
+ constructor(container, options, enhancedSearchUrl) {
7
+ super(container, options)
8
+ this.renderSuggestionGroup = this.renderSuggestionGroup.bind(this)
9
+ this.enhancedSearchUrl = enhancedSearchUrl
10
+ this.createHtml()
11
+ this.render()
12
+ }
13
+
14
+ renderSuggestionChip = (term) => {
15
+ return `<a
16
+ data-trackable="link"
17
+ data-suggestion-id="${term}"
18
+ href="${this.enhancedSearchUrl}${term}"
19
+ class="n-topic-search__target enhanced-search__chip">
20
+ <span class="enhanced-search__chip-text">${term}</span>
21
+ </a>`
22
+ }
23
+
24
+ renderDefaultSuggestionsChips() {
25
+ return `
26
+ <div class="enhanced-search__default-results">
27
+ ${['US China trade dispute', 'When is UK inflation likely to fall?', 'Greenwashing']
28
+ .map(this.renderSuggestionChip)
29
+ .join('')}
30
+ </div>`
31
+ }
32
+
33
+ renderSuggestionLink(suggestion, group) {
34
+ return `<li class="n-topic-search__item">
35
+ <a class="n-topic-search__target enhanced-search__target ${group.linkClassName}"
36
+ href="${suggestion.url}"
37
+ tabindex="0"
38
+ data-trackable="link"
39
+ data-suggestion-id="${suggestion.id}"
40
+ data-suggestion-type="${suggestion.type}"
41
+ >${suggestion.html}</a>
42
+ </li>`
43
+ }
44
+
45
+ renderSuggestionGroup(group) {
46
+ let html = `<div class="enhanced-search__group ${group.linkClassName}" data-trackable="${group.trackable}">`
47
+
48
+ if (group.suggestions.length || group.emptyHtml) {
49
+ html += `<ul class="n-topic-search__item-list">
50
+ ${group.suggestions.map((suggestion) => this.renderSuggestionLink(suggestion, group)).join('')}
51
+ </ul>`
52
+ }
53
+ html += '</div>'
54
+ return html
55
+ }
56
+
57
+ createHtml() {
58
+ const term = this.state.searchTerm
59
+ const hasConcepts = this.state.suggestions.concepts && this.state.suggestions.concepts.length
60
+ const hasEquities = this.state.suggestions.equities && this.state.suggestions.equities.length
61
+ const hasSuggestions = hasConcepts || hasEquities
62
+ const suggestions = []
63
+ this.items = []
64
+ if (this.options.categories.includes('concepts')) {
65
+ suggestions.push({
66
+ linkClassName: 'enhanced-search__target--news',
67
+ trackable: 'enhanced-search-news',
68
+ suggestions: this.state.suggestions.concepts.slice(0, DISPLAY_ITEMS).map((suggestion) => ({
69
+ html: this.highlight(suggestion.prefLabel),
70
+ url: suggestion.url,
71
+ id: suggestion.id,
72
+ type: 'enhanced-search-tag'
73
+ }))
74
+ })
75
+ }
76
+
77
+ if (this.options.categories.includes('equities')) {
78
+ suggestions.push({
79
+ trackable: 'enhanced-search-equities',
80
+ linkClassName: 'enhanced-search__target--equities',
81
+ emptyHtml: '<div className="enhanced-search__no-results-message">No equities found</div>',
82
+ suggestions: this.state.suggestions.equities.slice(0, DISPLAY_ITEMS).map((suggestion) => ({
83
+ html: `<span class="enhanced-search__target__equity-name">${this.highlight(
84
+ suggestion.name
85
+ )}</span><abbr>${this.highlight(suggestion.symbol)}</abbr>`,
86
+ url: suggestion.url,
87
+ id: suggestion.symbol,
88
+ type: 'enhanced-search-equity'
89
+ }))
90
+ })
91
+ }
92
+
93
+ this.newHtml = `
94
+ <div aria-live="assertive">
95
+ ${
96
+ hasSuggestions
97
+ ? `
98
+ <div
99
+ class="o-normalise-visually-hidden n-topic-search__suggestions_explanation"
100
+ >
101
+ Search results have been displayed. To jump to the list of suggestions press
102
+ the down arrow key.
103
+ </div>
104
+ `
105
+ : ''
106
+ }
107
+ <div
108
+ class="n-topic-search n-topic-search__suggestions"
109
+ data-trackable="typeahead"
110
+ >
111
+ <div class="enhanced-search__wrapper">
112
+ <h3 class="enhanced-search__title">Enhanced search results for...</h3>
113
+ ${term ? this.renderSuggestionChip(term) : this.renderDefaultSuggestionsChips()}
114
+ <div class="o-normalise-visually-hidden">Suggestions include</div>
115
+ ${
116
+ hasSuggestions
117
+ ? `<div class="enhanced-search__suggestions">
118
+ ${suggestions.map(this.renderSuggestionGroup).join('')}
119
+ </div>
120
+ `
121
+ : ''
122
+ }
123
+ </div>
124
+ </div>
125
+ </div>`
126
+ }
127
+
128
+ handleSelection(el, ev) {
129
+ ev.stopPropagation()
130
+ // we don't prevent default as the link's url is a link to the relevant stream page
131
+ return
132
+ }
133
+ }
134
+
135
+ export default CustomSuggestionList
@@ -0,0 +1,31 @@
1
+ import TopicSearch from 'n-topic-search'
2
+ import CustomSuggestionList from './customList'
3
+
4
+ class EnhancedSearch extends TopicSearch {
5
+ constructor(containerEl, options) {
6
+ super(containerEl, {
7
+ ...options,
8
+ listComponent: (...args) => new CustomSuggestionList(...args.concat(options?.enhancedSearchUrl))
9
+ })
10
+
11
+ const inputs = [
12
+ document.querySelector('#o-header-search-term-primary'),
13
+ document.querySelector('#o-header-search-term-sticky'),
14
+ document.querySelector('#o-header-drawer-search-term')
15
+ ]
16
+ inputs[0].parentElement.setAttribute('data-attribute-enhanced-search', 'true')
17
+ inputs.forEach((input) =>
18
+ input.setAttribute('placeholder', 'Search the FT using questions, topics or article titles')
19
+ )
20
+ }
21
+
22
+ onFocus(ev) {
23
+ super.onFocus(ev)
24
+ this.show()
25
+ this.suggestionTargets = Array.from(
26
+ this.suggestionListContainer.querySelectorAll('.n-topic-search__target')
27
+ )
28
+ }
29
+ }
30
+
31
+ export default EnhancedSearch
@@ -0,0 +1,117 @@
1
+ @use '@financial-times/o-colors/main' as colors;
2
+ @use '@financial-times/o-spacing/main' as spacing;
3
+ @use '@financial-times/o-typography/main' as typography;
4
+
5
+ @mixin enhancedSearch {
6
+ .enhanced-search {
7
+ &__wrapper {
8
+ display: flex;
9
+ flex-direction: column;
10
+ width: 100%;
11
+ padding: spacing.oSpacingByName('s6');
12
+ }
13
+
14
+ &__title {
15
+ color: colors.oColorsByName('black');
16
+ @include typography.oTypographySans($scale: 0, $style: 'normal', $weight: 'semibold');
17
+ margin: 0 0 spacing.oSpacingByName('s3') 0;
18
+ }
19
+
20
+ &__suggestions {
21
+ display: flex;
22
+ gap: spacing.oSpacingByName('m16');
23
+ padding-top: spacing.oSpacingByName('s6');
24
+ flex-direction: column;
25
+
26
+ @include oGridRespondTo('L') {
27
+ flex-direction: row;
28
+ }
29
+ }
30
+
31
+ &__target {
32
+ padding: 0;
33
+ padding-block: spacing.oSpacingByName('s2');
34
+ }
35
+
36
+ &__target::before,
37
+ &__chip::before {
38
+ border-bottom: unset !important;
39
+ }
40
+
41
+ &__target:hover,
42
+ &__target:focus {
43
+ background-color: transparent;
44
+ color: oColorsByName('teal');
45
+ }
46
+
47
+ &__target:hover:before,
48
+ &__target:focus:before {
49
+ opacity: 0;
50
+ }
51
+
52
+ &__target--news {
53
+ color: colors.oColorsByName('claret-70');
54
+ }
55
+
56
+ &__target--news:hover,
57
+ &__target--news:focus {
58
+ color: colors.oColorsByName('claret-30');
59
+ }
60
+
61
+ &__target--equities {
62
+ display: flex;
63
+ align-items: flex-start;
64
+ gap: spacing.oSpacingByName('s3');
65
+ color: colors.oColorsByName('black');
66
+ }
67
+
68
+ &__target--equities:hover,
69
+ &__target--equities:focus {
70
+ color: colors.oColorsByName('slate');
71
+ }
72
+
73
+ &__chip {
74
+ border-radius: spacing.oSpacingByName('s1');
75
+ background: colors.oColorsMix('ft-grey', 'white', 10);
76
+ padding: spacing.oSpacingByName('s2') spacing.oSpacingByName('s3');
77
+ width: fit-content;
78
+ cursor: pointer;
79
+ text-decoration: none;
80
+ display: block;
81
+ }
82
+
83
+ &__chip:hover,
84
+ &__chip:focus {
85
+ background: colors.oColorsMix('ft-grey', 'white', 20);
86
+ }
87
+
88
+ &__chip:focus-visible {
89
+ box-shadow: 0 0 0 spacing.oSpacingByIncrement(0.5) colors.oColorsByName('white'),
90
+ 0 0 0 spacing.oSpacingByName('s1') colors.oColorsByName('black-70');
91
+ color: colors.oColorsByName('white');
92
+ border-color: transparent;
93
+ border-radius: 0;
94
+ outline: none;
95
+ }
96
+
97
+ &__chip-text {
98
+ margin: 0;
99
+ color: colors.oColorsByName('black');
100
+ @include typography.oTypographySans($scale: -1, $style: 'normal', $weight: 'regular');
101
+ outline: none;
102
+ }
103
+
104
+ &__default-results {
105
+ display: flex;
106
+ justify-content: flex-start;
107
+ align-items: flex-start;
108
+ gap: spacing.oSpacingByName('s2');
109
+ flex-direction: column;
110
+
111
+ @include oGridRespondTo('L') {
112
+ flex-direction: row;
113
+ align-items: center;
114
+ }
115
+ }
116
+ }
117
+ }
package/src/index.tsx CHANGED
@@ -48,7 +48,9 @@ function MainHeader(props: THeaderProps) {
48
48
  {props.showLogoLink ? <TopColumnCenter /> : <TopColumnCenterNoLink />}
49
49
  <TopColumnRight {...props} />
50
50
  </TopWrapper>
51
+
51
52
  <Search instance="primary" />
53
+
52
54
  <MobileNav {...props} />
53
55
  <NavDesktop>
54
56
  <NavListLeft {...props} />
package/styles.scss CHANGED
@@ -1,14 +1,18 @@
1
1
  // This will be overridden by dotcom-ui-layout, it's necessary here for storybook builds
2
2
  $system-code: 'page-kit-header' !default;
3
3
 
4
- @import "n-ui-foundations/mixins";
4
+
5
+ @import 'n-ui-foundations/mixins';
5
6
 
6
7
  // We don't need the sub-brand or transparent header styles so disable them.
7
8
  // TODO: move drawer styles into a separate stylesheet which can be lazy loaded?
8
- @import "@financial-times/o-header/main";
9
+ @import '@financial-times/o-header/main';
9
10
  @include oHeader(('top', 'nav', 'subnav', 'search', 'megamenu', 'drawer', 'anon', 'sticky', 'simple'));
10
11
 
11
- @import "src/header";
12
+ @import 'src/header';
12
13
 
13
- @import "n-topic-search/main";
14
+ @import 'n-topic-search/main';
14
15
  @include nTopicSearch;
16
+
17
+ @import './src/enhanced-search/styles';
18
+ @include enhancedSearch;