@ilo-org/twig 1.0.1 → 1.0.3

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.
Files changed (29) hide show
  1. package/dist/components/accordion/accordion-item.twig +2 -2
  2. package/dist/components/accordion/accordion.behavior.js +1 -1
  3. package/dist/components/accordion/accordion.css +1 -1
  4. package/dist/components/breadcrumb/breadcrumb.behavior.js +1 -1
  5. package/dist/components/breadcrumb/breadcrumb.css +1 -1
  6. package/dist/components/breadcrumb/breadcrumb.twig +48 -36
  7. package/dist/components/breadcrumb/breadcrumb.wingsuit.yml +7 -8
  8. package/dist/components/contextmenu/contextmenu.css +1 -1
  9. package/dist/components/contextmenu/contextmenu.twig +9 -9
  10. package/dist/components/herocard/herocard.css +1 -1
  11. package/dist/components/logogrid/logogrid.wingsuit.yml +1 -1
  12. package/dist/components/navigation/navigation.css +1 -1
  13. package/dist/components/promocard/promocard.css +1 -1
  14. package/dist/components/richtext/richtext.css +1 -1
  15. package/dist/components/statcard/statcard.css +1 -1
  16. package/dist/components/tabs/tabs.css +1 -1
  17. package/dist/components/tabs/tabs.twig +4 -1
  18. package/dist/components/tabs/tabs.wingsuit.yml +3 -134
  19. package/dist/components/textcard/textcard.css +1 -1
  20. package/package.json +6 -6
  21. package/src/patterns/components/accordion/accordion-item.twig +2 -2
  22. package/src/patterns/components/accordion/accordion.js +3 -3
  23. package/src/patterns/components/breadcrumb/breadcrumb.js +176 -22
  24. package/src/patterns/components/breadcrumb/breadcrumb.twig +48 -36
  25. package/src/patterns/components/breadcrumb/breadcrumb.wingsuit.yml +7 -8
  26. package/src/patterns/components/contextmenu/contextmenu.twig +9 -9
  27. package/src/patterns/components/logogrid/logogrid.wingsuit.yml +1 -1
  28. package/src/patterns/components/tabs/tabs.twig +4 -1
  29. package/src/patterns/components/tabs/tabs.wingsuit.yml +3 -134
@@ -7,9 +7,10 @@ tabs:
7
7
  items:
8
8
  type: object
9
9
  label: Items
10
- description: The items and labels for each tab
10
+ description: The items and labels for each tab. Each item takes a `label` property which gives the tag a name, a `component` property which specifies the kind of component the tab should render, and the `componentdata` which contains the data that will be passed to that component. You can also include an optional `icon` component with the name of the icon to render next to the label.
11
11
  preview:
12
12
  - label: "Tab Label With A Really Really Lenghty Title Even Though We Do Not Recommend Such A Lengthy Title"
13
+ icon: "info"
13
14
  component: "image"
14
15
  componentdata:
15
16
  alt: "My alt text"
@@ -33,137 +34,5 @@ tabs:
33
34
  default:
34
35
  label: Default
35
36
  description: the default settings for the Tabs
36
- withicon:
37
- label: With Icon
38
- description: Tabs with Icons
39
- fields:
40
- items:
41
- - label: "Tab One"
42
- component: "image"
43
- componentdata:
44
- alt: "My alt text"
45
- caption: "my image caption"
46
- credit: "Photo: copyright 2022 Person S. Name"
47
- url:
48
- - breakpoint: 0
49
- src: "https://place-hold.it/400x300?text=Tab One Image"
50
- - breakpoint: 800
51
- src: "https://place-hold.it/800x600?text=Tab One tImage"
52
- - breakpoint: 1200
53
- src: "https://place-hold.it/1200x900?text=Tab One Image"
54
- - breakpoint: 1440
55
- src: "https://place-hold.it/1600x1200?text=Tab One Image"
56
- icon: error
57
- - label: "Tab Two"
58
- component: "image"
59
- componentdata:
60
- alt: "My alt text"
61
- caption: "my image caption"
62
- credit: "Photo: copyright 2022 Person S. Name"
63
- url:
64
- - breakpoint: 0
65
- src: "https://place-hold.it/400x300?text=Tab Two Image"
66
- - breakpoint: 800
67
- src: "https://place-hold.it/800x600?text=Tab Two Image"
68
- - breakpoint: 1200
69
- src: "https://place-hold.it/1200x900?text=Tab Two Image"
70
- - breakpoint: 1440
71
- src: "https://place-hold.it/1600x1200?text=Tab Two Image"
72
- icon: error
73
- - label: "Tab Three"
74
- component: "image"
75
- componentdata:
76
- alt: "My alt text"
77
- caption: "my image caption"
78
- credit: "Photo: copyright 2022 Person S. Name"
79
- url:
80
- - breakpoint: 0
81
- src: "https://place-hold.it/400x300?text=Tab Three Image"
82
- - breakpoint: 800
83
- src: "https://place-hold.it/800x600?text=Tab Three Image"
84
- - breakpoint: 1200
85
- src: "https://place-hold.it/1200x900?text=Tab Three Image"
86
- - breakpoint: 1440
87
- src: "https://place-hold.it/1600x1200?text=Tab Three Image"
88
- icon: error
89
- fiveitems:
90
- label: Five Items
91
- description: Tab component with five items
92
- fields:
93
- items:
94
- - label: "Tab One"
95
- component: "image"
96
- componentdata:
97
- alt: "My alt text"
98
- caption: "my image caption"
99
- credit: "Photo: copyright 2022 Person S. Name"
100
- url:
101
- - breakpoint: 0
102
- src: "https://place-hold.it/400x300?text=Tab One Image"
103
- - breakpoint: 800
104
- src: "https://place-hold.it/800x600?text=Tab One tImage"
105
- - breakpoint: 1200
106
- src: "https://place-hold.it/1200x900?text=Tab One Image"
107
- - breakpoint: 1440
108
- src: "https://place-hold.it/1600x1200?text=Tab One Image"
109
- - label: "Tab Two"
110
- component: "image"
111
- componentdata:
112
- alt: "My alt text"
113
- caption: "my image caption"
114
- credit: "Photo: copyright 2022 Person S. Name"
115
- url:
116
- - breakpoint: 0
117
- src: "https://place-hold.it/400x300?text=Tab Two Image"
118
- - breakpoint: 800
119
- src: "https://place-hold.it/800x600?text=Tab Two Image"
120
- - breakpoint: 1200
121
- src: "https://place-hold.it/1200x900?text=Tab Two Image"
122
- - breakpoint: 1440
123
- src: "https://place-hold.it/1600x1200?text=Tab Two Image"
124
- - label: "Tab Three"
125
- component: "image"
126
- componentdata:
127
- alt: "My alt text"
128
- caption: "my image caption"
129
- credit: "Photo: copyright 2022 Person S. Name"
130
- url:
131
- - breakpoint: 0
132
- src: "https://place-hold.it/400x300?text=Tab Three Image"
133
- - breakpoint: 800
134
- src: "https://place-hold.it/800x600?text=Tab Three Image"
135
- - breakpoint: 1200
136
- src: "https://place-hold.it/1200x900?text=Tab Three Image"
137
- - breakpoint: 1440
138
- src: "https://place-hold.it/1600x1200?text=Tab Three Image"
139
- - label: "Tab Four Has A Really Lenghthy Title Which Might Get Truncated"
140
- component: "image"
141
- componentdata:
142
- alt: "My alt text"
143
- caption: "my image caption"
144
- credit: "Photo: copyright 2022 Person S. Name"
145
- url:
146
- - breakpoint: 0
147
- src: "https://place-hold.it/400x300?text=Tab Four Image"
148
- - breakpoint: 800
149
- src: "https://place-hold.it/800x600?text=Tab Four Image"
150
- - breakpoint: 1200
151
- src: "https://place-hold.it/1200x900?text=Tab Four Image"
152
- - breakpoint: 1440
153
- src: "https://place-hold.it/1600x1200?text=Tab Four Image"
154
- - label: "Tab Five"
155
- component: "image"
156
- componentdata:
157
- alt: "My alt text"
158
- caption: "my image caption"
159
- credit: "Photo: copyright 2022 Person S. Name"
160
- url:
161
- - breakpoint: 0
162
- src: "https://place-hold.it/400x300?text=Tab Five Image"
163
- - breakpoint: 800
164
- src: "https://place-hold.it/800x600?text=Tab Five Image"
165
- - breakpoint: 1200
166
- src: "https://place-hold.it/1200x900?text=Tab Five Image"
167
- - breakpoint: 1440
168
- src: "https://place-hold.it/1600x1200?text=Tab Five Image"
37
+
169
38
  visibility: storybook
@@ -1 +1 @@
1
- .ilo--card__type__text{--max-width:16.1307609861rem;border-bottom:.1607717042rem solid #b8c4cc;padding:2.1436227224rem 1.2861736334rem 1.7148981779rem}.ilo--card__type__text,[dir=rtl] .ilo--card__type__text{clip-path:polygon(0 0,calc(100% - 72px) 0,100% 40px,100% 100%,0 100%)}[dir=rtl] .ilo--card__type__text{clip-path:polygon(72px 0,100% 0,100% 100%,0 100%,0 40px)}.ilo--card__type__text [class$=profile__theme__light] *{color:#2d2d2d}.ilo--card__type__text [class$=profile__theme__dark] *{color:#fff}.ilo--card__type__text:focus,.ilo--card__type__text:focus-within,.ilo--card__type__text:hover{filter:drop-shadow(0 -4px 16px rgba(30,45,190,.05)) drop-shadow(0 10px 20px rgba(30,45,190,.08)) drop-shadow(0 4px 8px rgba(30,45,190,.05)) drop-shadow(0 .8px 1.6px rgba(30,45,190,.04));transition-property:border-color;transition-duration:.15s;transition-timing-function:ease-out;border-color:#1e2dbe}.ilo--card__type__text:focus-within [class*=profile__theme] *,.ilo--card__type__text:focus [class*=profile__theme] *,.ilo--card__type__text:hover [class*=profile__theme] *{color:#1e2dbe}@media screen and (max-width:609px){.ilo--card__type__text{--max-width:100%}}.ilo--card__type__text.ilo--card__size__fluid,.ilo--card__type__text.ilo--card__size__wide{--max-width:28.0278670954rem;padding:2.1436227224rem 1.7148981779rem 1.7148981779rem}.ilo--card__type__text.ilo--card__size__narrow{--max-width:16.1307609861rem;padding:2.1436227224rem 1.2861736334rem 1.7148981779rem}@media screen and (max-width:609px){.ilo--card__type__text.ilo--card__size__narrow{--max-width:100%}}.ilo--card__type__text.ilo--card__size__narrow .ilo--card--title{font-size:18.66px;letter-spacing:-.035em;line-height:24.26px;margin-bottom:1.674953518rem}.ilo--card__type__text.ilo--card__dark{border-bottom:.1607717042rem solid #fa3c4b}.ilo--card__type__text .ilo--card--eyebrow{margin-bottom:1.0110920672rem}.ilo--card__type__text .ilo--card--title{font-size:18.66px;letter-spacing:-.035em;line-height:24.26px;margin-bottom:1.674953518rem}@media screen and (min-width:610px){.ilo--card__type__text .ilo--card--title{font-size:23.32px;letter-spacing:-.035em;line-height:29.15px;margin-bottom:1.5763397642rem}}.ilo--card__type__text .ilo--card--date{display:block;margin-bottom:1.4398166117rem}.ilo--card__type__text .ilo--card--content{display:flex;flex-direction:column;position:relative}.ilo--card__type__text .ilo--card--content>*{justify-self:flex-start}.ilo--card__type__text .ilo--card--content>:last-child{justify-self:flex-end;margin-bottom:0}
1
+ .ilo--card__type__text{--max-width:16.1307609861rem;border-bottom:.1607717042rem solid #b8c4cc;padding:2.1436227224rem 1.2861736334rem 1.7148981779rem;clip-path:polygon(0 0,calc(100% - 72px) 0,100% 40px,100% 100%,0 100%)}[dir=rtl] .ilo--card__type__text{clip-path:polygon(72px 0,100% 0,100% 100%,0 100%,0 40px)}.ilo--card__type__text [class$=profile__theme__light] *{color:#2d2d2d}.ilo--card__type__text [class$=profile__theme__dark] *{color:#fff}.ilo--card__type__text:focus,.ilo--card__type__text:focus-within,.ilo--card__type__text:hover{filter:drop-shadow(0 -4px 16px rgba(30,45,190,.05)) drop-shadow(0 10px 20px rgba(30,45,190,.08)) drop-shadow(0 4px 8px rgba(30,45,190,.05)) drop-shadow(0 .8px 1.6px rgba(30,45,190,.04));transition-property:border-color;transition-duration:.15s;transition-timing-function:ease-out;border-color:#1e2dbe}.ilo--card__type__text:focus-within [class*=profile__theme] *,.ilo--card__type__text:focus [class*=profile__theme] *,.ilo--card__type__text:hover [class*=profile__theme] *{color:#1e2dbe}@media screen and (max-width:609px){.ilo--card__type__text{--max-width:100%}}.ilo--card__type__text.ilo--card__size__fluid,.ilo--card__type__text.ilo--card__size__wide{--max-width:28.0278670954rem;padding:2.1436227224rem 1.7148981779rem 1.7148981779rem}.ilo--card__type__text.ilo--card__size__narrow{--max-width:16.1307609861rem;padding:2.1436227224rem 1.2861736334rem 1.7148981779rem}@media screen and (max-width:609px){.ilo--card__type__text.ilo--card__size__narrow{--max-width:100%}}.ilo--card__type__text.ilo--card__size__narrow .ilo--card--title{font-size:18.66px;letter-spacing:-.035em;line-height:24.26px;margin-bottom:1.674953518rem}.ilo--card__type__text.ilo--card__dark{border-bottom:.1607717042rem solid #fa3c4b}.ilo--card__type__text .ilo--card--eyebrow{margin-bottom:1.0110920672rem}.ilo--card__type__text .ilo--card--title{font-size:18.66px;letter-spacing:-.035em;line-height:24.26px;margin-bottom:1.674953518rem}@media screen and (min-width:610px){.ilo--card__type__text .ilo--card--title{font-size:23.32px;letter-spacing:-.035em;line-height:29.15px;margin-bottom:1.5763397642rem}}.ilo--card__type__text .ilo--card--date{display:block;margin-bottom:1.4398166117rem}.ilo--card__type__text .ilo--card--content{display:flex;flex-direction:column;position:relative}.ilo--card__type__text .ilo--card--content>*{justify-self:flex-start}.ilo--card__type__text .ilo--card--content>:last-child{justify-self:flex-end;margin-bottom:0}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ilo-org/twig",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Twig components for the ILO's Design System",
6
6
  "publishConfig": {
@@ -49,10 +49,10 @@
49
49
  "video.js": "^7.21.2",
50
50
  "videojs-youtube": "^3.0.1",
51
51
  "@ilo-org/brand-assets": "0.5.1",
52
- "@ilo-org/fonts": "0.2.1",
52
+ "@ilo-org/fonts": "0.2.2",
53
53
  "@ilo-org/icons": "0.3.1",
54
- "@ilo-org/styles": "1.0.1",
55
- "@ilo-org/themes": "0.8.1",
54
+ "@ilo-org/styles": "1.0.3",
55
+ "@ilo-org/themes": "0.9.0",
56
56
  "@ilo-org/utils": "0.1.1"
57
57
  },
58
58
  "devDependencies": {
@@ -107,8 +107,8 @@
107
107
  "webpack": "^4.46.0",
108
108
  "yaml-loader": "0.6.0",
109
109
  "yo": "^3.1.1",
110
- "@ilo-org/eslint-config": "0.2.1",
111
- "@ilo-org/prettier-config": "0.1.1"
110
+ "@ilo-org/eslint-config": "1.0.0",
111
+ "@ilo-org/prettier-config": "1.0.0"
112
112
  },
113
113
  "scripts": {
114
114
  "storybook": "node importprefix.js && node importsvgs.js && start-storybook --config-dir apps/storybook",
@@ -5,12 +5,12 @@
5
5
  {% set accordion_id = id ~ uid %}
6
6
  {% set button_id = 'button-' ~ accordion_id %}
7
7
  {% set panel_id = 'panel-' ~ accordion_id %}
8
- {% set expanded_class = defaultExpanded ? prefix ~ '--accordion--panel--open' : '' %}
8
+ {% set expanded_class = defaultExpanded ? prefix ~ '--accordion--panel__open' : '' %}
9
9
  {% set scroll_class = scroll ? prefix ~ '--accordion--panel__scroll' : '' %}
10
10
 
11
11
  <li class="{{prefix}}--accordion--item" id="{{ accordion_id }}">
12
12
  <div class="ilo--h3">
13
- <button class="{{prefix}}--accordion--button {{prefix}}--accordion--button--{{ size|default('small') }}" aria-expanded="{{ defaultExpanded }}" aria-controls="{{ panel_id }}" id="{{ button_id }}">
13
+ <button class="{{prefix}}--accordion--button {{prefix}}--accordion--button__{{ size|default('small') }}" aria-expanded="{{ defaultExpanded }}" aria-controls="{{ panel_id }}" id="{{ button_id }}">
14
14
  {{label}}
15
15
  </button>
16
16
  </div>
@@ -130,7 +130,7 @@ export default class Accordion {
130
130
  const panel = panelbutton
131
131
  .closest(".ilo--accordion--item")
132
132
  .querySelector(".ilo--accordion--panel");
133
- const isopen = panel.classList.contains("ilo--accordion--panel--open");
133
+ const isopen = panel.classList.contains("ilo--accordion--panel__open");
134
134
 
135
135
  if (!this.multipleExpanded) {
136
136
  this.accordionPanels.forEach((item) => {
@@ -158,7 +158,7 @@ export default class Accordion {
158
158
  * @chainable
159
159
  */
160
160
  collapseSection(element) {
161
- element.classList.remove("ilo--accordion--panel--open");
161
+ element.classList.remove("ilo--accordion--panel__open");
162
162
  element.parentElement
163
163
  .querySelector(".ilo--accordion--button")
164
164
  .setAttribute(ARIA.EXPANDED, "false");
@@ -178,7 +178,7 @@ export default class Accordion {
178
178
  .querySelector(".ilo--accordion--button")
179
179
  .setAttribute(ARIA.EXPANDED, "true");
180
180
  element.setAttribute(ARIA.HIDDEN, "false");
181
- element.classList.add("ilo--accordion--panel--open");
181
+ element.classList.add("ilo--accordion--panel__open");
182
182
  this.handleTabIndex(element, "ADD");
183
183
  }
184
184
 
@@ -45,12 +45,35 @@ export default class Breadcrumb {
45
45
  * @chainable
46
46
  */
47
47
  cacheDomReferences() {
48
- this.breadcrumbs = this.element.querySelector(".ilo--breadcrumb--items");
49
- this.breadcrumbwidth = this.breadcrumbs.offsetWidth;
50
- this.ContextButton = this.element.querySelector(
51
- `.${this.prefix}--breadcrumb--item--context`
48
+ // Store references to classnames
49
+ this.breadcrumbsId = `${this.prefix}--breadcrumb--container`;
50
+ this.contextAreaClass = `${this.prefix}--breadcrumb--context`;
51
+ this.contextMenuItemsClass = `${this.prefix}--context-menu`;
52
+ this.contextCollapseClass = `${this.contextAreaClass}__collapse`;
53
+ this.contextButtonClass = `${this.contextAreaClass}--button`;
54
+ this.contextMenuClass = `${this.contextAreaClass}--menu`;
55
+ this.contextMenuVisibleClass = `${this.contextMenuClass}__visible`;
56
+
57
+ // Store reference to DOM elements
58
+ this.breadcrumbs = this.element.querySelector(`#${this.breadcrumbsId}`);
59
+ this.contextArea = this.element.querySelector(`.${this.contextAreaClass}`);
60
+ this.contextMenu = this.element.querySelector(`.${this.contextMenuClass}`);
61
+ this.contextButton = this.element.querySelector(
62
+ `.${this.contextButtonClass}`
63
+ );
64
+ this.breadcrumbsLastLink = this.element.querySelector(
65
+ `#${this.breadcrumbsId} > li:last-child a`
66
+ );
67
+ this.contextMenuItems = this.element.querySelector(
68
+ `.${this.contextMenuItemsClass}`
52
69
  );
53
- this.ContextMenu = this.element.querySelector(`.context--menu`);
70
+ this.contextMenuItemFirstLink =
71
+ this.contextMenuItems.querySelector("li:first-child a");
72
+ this.contextMenuItemLastLink =
73
+ this.contextMenuItems.querySelector("li:last-child a");
74
+
75
+ // Store a reference to the breadcrumbs width so we know when to uncollapse them
76
+ this.breadcrumbsWidth = this.breadcrumbs.offsetWidth;
54
77
 
55
78
  return this;
56
79
  }
@@ -64,59 +87,190 @@ export default class Breadcrumb {
64
87
  setupHandlers() {
65
88
  this.onResize = this.onResize.bind(this);
66
89
  this.onClick = this.onClick.bind(this);
90
+ this.contexMenuIsOpen = this.contexMenuIsOpen.bind(this);
91
+ this.openContextMenu = this.openContextMenu.bind(this);
92
+ this.closeContextMenu = this.closeContextMenu.bind(this);
93
+ this.focusBreadcrumbsLastLink = this.focusBreadcrumbsLastLink.bind(this);
94
+ this.focusContextMenuItemFirstLink =
95
+ this.focusContextMenuItemFirstLink.bind(this);
96
+ this.onKeydown = this.onKeydown.bind(this);
67
97
 
68
98
  return this;
69
99
  }
70
100
 
71
101
  /**
72
- * Creates event listeners to enable interaction with view
102
+ * Adds event listeners to enable interaction with view
73
103
  *
74
104
  * @return {Object} Breadcrumb A reference to the instance of the class
75
105
  * @chainable
76
106
  */
77
107
  enable() {
108
+ // Handle resizing events
78
109
  window.addEventListener(EVENTS.RESIZE, (e) => this.onResize(e));
79
- if (this.ContextButton) {
80
- this.ContextButton.addEventListener(EVENTS.CLICK, () => this.onClick());
110
+
111
+ if (this.contextButton) {
112
+ // When the context button gets clicked
113
+ this.contextButton.addEventListener(EVENTS.CLICK, (e) => this.onClick(e));
114
+
115
+ // We only need the keydown handler to manage tab navigation when the
116
+ // context menu is visible
117
+ this.element.addEventListener("keydown", (e) => this.onKeydown(e));
118
+
119
+ return this;
81
120
  }
121
+ }
82
122
 
123
+ /**
124
+ * OnClick interaction with the ContextMenu button
125
+ *
126
+ * @return {Object} Breadcrumb A reference to the instance of the class
127
+ * @chainable
128
+ */
129
+ onClick(e) {
130
+ e.stopPropagation();
131
+ if (this.contexMenuIsOpen()) {
132
+ this.closeContextMenu();
133
+ } else {
134
+ this.openContextMenu();
135
+ // Add an event listener to close the context menu if the user clicks outside of it
136
+ window.addEventListener("click", this.closeContextMenu, { once: true });
137
+ }
83
138
  return this;
84
139
  }
85
140
 
86
141
  /**
87
- * onResize interaction with the accordion button
142
+ * onResize interaction
88
143
  *
89
144
  * @return {Object} Breadcrumb A reference to the instance of the class
90
145
  * @chainable
91
146
  */
92
147
  onResize() {
93
- if (this.ContextMenu) {
94
- if (this.breadcrumbwidth > this.element.offsetWidth / 2) {
95
- this.element.classList.add("contextmenu");
96
- this.ContextMenu.classList.remove("open");
148
+ if (this.contextArea && this.breadcrumbsWidth) {
149
+ if (this.breadcrumbsWidth >= window.innerWidth / 1.5) {
150
+ this.contextArea.classList.add(this.contextCollapseClass);
97
151
  } else {
98
- this.element.classList.remove("contextmenu");
99
- this.ContextMenu.classList.remove("open");
152
+ this.contextArea.classList.remove(this.contextCollapseClass);
153
+ this.closeContextMenu();
100
154
  }
101
155
  }
102
156
  return this;
103
157
  }
104
158
 
105
159
  /**
106
- * OnClick interaction with the ContextMenu button
160
+ * onKeydown interaction for tab navigation
107
161
  *
162
+ * @param {*} e keydown event
108
163
  * @return {Object} Breadcrumb A reference to the instance of the class
109
164
  * @chainable
110
165
  */
111
- onClick() {
112
- if (this.ContextMenu) {
113
- if (this.ContextMenu.classList.contains("open")) {
114
- this.ContextMenu.classList.remove("open");
115
- } else {
116
- this.ContextMenu.classList.add("open");
166
+ onKeydown(e) {
167
+ // Back navigation using the shift key
168
+ if (e.shiftKey) {
169
+ if (e.key.toLowerCase() === "tab") {
170
+ // If the first context menu item is selected, tabbing back
171
+ // should select the context button
172
+ if (document.activeElement === this.contextMenuItemFirstLink) {
173
+ e.preventDefault();
174
+ this.contextButton.focus();
175
+ }
117
176
  }
177
+ return this;
118
178
  }
119
179
 
180
+ // When using tab navigation
181
+ if (e.key.toLowerCase() === "tab") {
182
+ // If the context menu is open...
183
+ const contexMenuIsOpen = this.contexMenuIsOpen();
184
+ // If the context button is focused then the next focusable item
185
+ // should be the first item in the context menu
186
+ if (contexMenuIsOpen) {
187
+ if (document.activeElement === this.contextButton) {
188
+ e.preventDefault();
189
+ this.focusContextMenuItemFirstLink();
190
+ }
191
+
192
+ // If the last context menu item is focused then the next focusable
193
+ // item should be the last breadcrumb item
194
+ if (document.activeElement === this.contextMenuItemLastLink) {
195
+ e.preventDefault();
196
+ this.focusBreadcrumbsLastLink();
197
+ this.closeContextMenu();
198
+ }
199
+ }
200
+ }
201
+
202
+ return this;
203
+ }
204
+
205
+ /**
206
+ * Position the context menu directly beneath the button
207
+ *
208
+ * @return {Object} Breadcrumb A reference to the instance of the class
209
+ * @chainable
210
+ */
211
+ positionContextMenu() {
212
+ const buttonRect = this.contextButton.getBoundingClientRect();
213
+ const buttonCenterX = buttonRect.left + buttonRect.width / 2;
214
+ const contextMenuWidth = this.contextMenu.offsetWidth;
215
+ const navStart = buttonCenterX - contextMenuWidth / 2;
216
+ const navTop = buttonRect.bottom + 16;
217
+ this.contextMenu.style.left = navStart + "px";
218
+ this.contextMenu.style.top = navTop + "px";
219
+ return this;
220
+ }
221
+
222
+ /**
223
+ * Open the Context menu
224
+ *
225
+ * @return {Object} Breadcrumb A reference to the instance of the class
226
+ * @chainable
227
+ */
228
+ openContextMenu() {
229
+ this.contextMenu.classList.add(this.contextMenuVisibleClass);
230
+ this.contextButton.setAttribute("aria-expanded", "true");
231
+ this.positionContextMenu();
232
+ return this;
233
+ }
234
+
235
+ /**
236
+ * Close the Context menu
237
+ *
238
+ * @return {Object} Breadcrumb A reference to the instance of the class
239
+ * @chainable
240
+ */
241
+ closeContextMenu() {
242
+ this.contextMenu.classList.remove(this.contextMenuVisibleClass);
243
+ this.contextButton.setAttribute("aria-expanded", "false");
244
+ }
245
+
246
+ /**
247
+ * Check to see whether the Context menu is open
248
+ *
249
+ * @returns boolean
250
+ */
251
+ contexMenuIsOpen() {
252
+ return this.contextMenu.classList.contains(this.contextMenuVisibleClass);
253
+ }
254
+
255
+ /**
256
+ * Focuses first link in the Context Menu
257
+ *
258
+ * @return {Object} Breadcrumb A reference to the instance of the class
259
+ * @chainable
260
+ */
261
+ focusContextMenuItemFirstLink() {
262
+ this.contextMenuItemFirstLink.focus();
263
+ return this;
264
+ }
265
+
266
+ /**
267
+ * Focuses last item of the Breadcrumbs
268
+ *
269
+ * @return {Object} Breadcrumb A reference to the instance of the class
270
+ * @chainable
271
+ */
272
+ focusBreadcrumbsLastLink() {
273
+ this.breadcrumbsLastLink.focus();
120
274
  return this;
121
275
  }
122
276
  }
@@ -1,43 +1,55 @@
1
- {#
2
- BREADCRUMB COMPONENT
3
- #}
1
+ {# breadcrumb.twig #}
2
+
3
+ {# If a home field is passed, it will be used as the first link #}
4
+ {# THE `HOME` FIELD WILL BE DEPRECATED IN FUTURE VERSIONS AND SHOULD NOT BE USED #}
5
+ {% if home %}
6
+ {% set firstlink = home %}
7
+ {% set contextLinks = links|slice(0, items|length - 1) %}
8
+ {% endif %}
9
+
10
+ {% if not home %}
11
+ {% set firstlink = links|first %}
12
+ {% set contextLinks = links|slice(1, items|length - 1) %}
13
+ {% endif %}
14
+
4
15
  {% set lastlink = links|last %}
5
- {% set firstlink = links|first %}
6
- <nav class="{{prefix}}--breadcrumb{% if className %} {{className}}{% endif %}" data-prefix="{{prefix}}" data-loadcomponent="Breadcrumb">
7
- <ol class="{{prefix}}--breadcrumb--items">
8
- <li class="{{prefix}}--breadcrumb--item home">
9
- <a class="{{prefix}}--breadcrumb--link" href="{{home.url}}">
10
- <span class="{{prefix}}--breadcrumb--link--label">{{home.label}}</span>
11
- </a>
12
- </li>
13
- {% if links|length >= 2 %}
14
- <li class="{{prefix}}--breadcrumb--item first">
15
- <a class="{{prefix}}--breadcrumb--link" href="{{firstlink.url}}">
16
- <span class="{{prefix}}--breadcrumb--link--label">{{firstlink.label}}</span>
17
- </a>
16
+
17
+ <nav class="{{prefix}}--breadcrumb {% if className %}{{className}}{% endif %}" data-prefix="{{prefix}}" data-loadcomponent="Breadcrumb">
18
+ <div class="{{prefix}}--breadcrumb--inner">
19
+ <ol class="{{prefix}}--breadcrumb--items" id="{{prefix}}--breadcrumb--container">
20
+ {# Render the first link on its own as a custom icon #}
21
+ <li class="{{prefix}}--breadcrumb--item {{prefix}}--breadcrumb--item__first">
22
+ <a aria-label="{{firstlink.label}}" class="{{prefix}}--breadcrumb--link" href="{{firstlink.url}}}"></a>
18
23
  </li>
19
- {% endif %}
20
- {% if links|length >= 3%}
21
- <li class="{{prefix}}--breadcrumb--item--context">
22
- <ul class="{{prefix}}--breadcrumb--items context--menu">
23
- {% for link in links %}
24
- {% if not loop.first and not loop.last %}
24
+ {# Render all but the first and last items in the context area #}
25
+ {% if contextLinks|length > 0 %}
26
+ <li class="{{prefix}}--breadcrumb--context">
27
+ <ol class="{{prefix}}--breadcrumb--items">
28
+ {% for link in contextLinks %}
25
29
  <li class="{{prefix}}--breadcrumb--item">
26
30
  <a href="{{link.url}}" class="{{prefix}}--breadcrumb--link">
27
- <span class="{{prefix}}--breadcrumb--link--label--dropdown {{prefix}}--breadcrumb--link--label">{{link.label}}</span>
31
+ <span class="{{prefix}}--breadcrumb--link--label">{{link.label}}</span>
28
32
  </a>
29
33
  </li>
30
- {% endif %}
31
- {% endfor %}
32
- </ul>
33
- </li>
34
- {% endif %}
35
- {% if links|length >= 1%}
36
- <li class="{{prefix}}--breadcrumb--item final">
37
- <a class="{{prefix}}--breadcrumb--link" href="{{lastlink.url}}">
38
- <span class="{{prefix}}--breadcrumb--link--label">{{lastlink.label}}</span>
39
- </a>
40
- </li>
41
- {% endif %}
42
- </ol>
34
+ {% endfor %}
35
+ </ol>
36
+ <button aria-label="{{buttonlabel|default('More links')}}" aria-expanded="false" controls="{{prefix}}--breadcrumb--menu" class="{{prefix}}--breadcrumb--context--button"></button>
37
+ </li>
38
+ {% endif %}
39
+ {# Render the last one outside the context area #}
40
+ {% if links|length > 1 %}
41
+ <li class="{{prefix}}--breadcrumb--item">
42
+ <a class="{{prefix}}--breadcrumb--link" href="{{lastlink.url}}">
43
+ <span class="{{prefix}}--breadcrumb--link--label">{{lastlink.label}}</span>
44
+ </a>
45
+ </li>
46
+ {% endif %}
47
+ </ol>
48
+ </div>
49
+ <div class="{{prefix}}--breadcrumb--context--menu" id="{{prefix}}--breadcrumb--menu">
50
+ {% include '@components/contextmenu/contextmenu.twig' with {
51
+ links: contextLinks,
52
+ prefix: prefix
53
+ } only %}
54
+ </div>
43
55
  </nav>
@@ -4,14 +4,13 @@ breadcrumb:
4
4
  label: Breadcrumb
5
5
  description: A component for displaying links in a "breadcrumb" ux
6
6
  fields:
7
- home:
8
- type: object
9
- label: Home
10
- description: url and label for link to site home page
11
- required: true
12
- preview:
13
- label: "Home"
14
- url: "/"
7
+ buttonlabel:
8
+ type: string
9
+ label: buttonlabel
10
+ description: The `aria-label` value for the button that toggles the context menu when the Breadcrumbs are collapsed
11
+ required: false
12
+ default: "Toggle links"
13
+ preview: "Toggle links"
15
14
  links:
16
15
  type: object
17
16
  label: Home
@@ -1,12 +1,12 @@
1
1
  {#
2
2
  CONTEXT MENU COMPONENT
3
3
  #}
4
- <ul class="{{prefix}}--context-menu">
5
- {% for link in links %}
6
- <li class="{{prefix}}--context-menu--item{% if link.endsection == 'true' %} endsection{% endif %}">
7
- <a href="{{link.url}}" class="{{prefix}}--context-menu--link">
8
- <span class="{{prefix}}--context-menu--label">{{link.label}}</span>
9
- </a>
10
- </li>
11
- {% endfor %}
12
- </ul>
4
+ <ol class="{{prefix}}--context-menu">
5
+ {% for link in links %}
6
+ <li class="{{prefix}}--context-menu--item{% if link.endsection == 'true' %} endsection{% endif %}">
7
+ <a href="{{link.url}}" class="{{prefix}}--context-menu--link">
8
+ <span class="{{prefix}}--context-menu--label">{{link.label}}</span>
9
+ </a>
10
+ </li>
11
+ {% endfor %}
12
+ </ol>
@@ -1,5 +1,5 @@
1
1
  logogrid:
2
- namespace: Components/Brand
2
+ namespace: Components/Media
3
3
  use: "@components/logogrid/logogrid.twig"
4
4
  label: Logo Grid
5
5
  description: The Logo Grid component renders a series of logos representing a group of organizations. It can be shown on a light or dark background.
@@ -10,7 +10,10 @@
10
10
  <a href="#tab--{{tabids[loop.index - 1]}}" class="{{prefix}}--tabs--selection--button{% if item.icon is defined %} icon{% endif %}" aria-controls="tab--{{tabids[loop.index - 1]}}" role="tab" tabindex="0" {% if loop.index == 1 %}aria-selected="true"{% else %}aria-selected="false"{% endif %} title="{{item.label}}">
11
11
  <span class="{{prefix}}--tabs--selection--label">{{item.label}}</span>
12
12
  {% if item.icon is defined %}
13
- {% include '@components/icon/icon.twig' with {icon: item.icon} %}
13
+ {% include '@components/icon/icon.twig' with {
14
+ name: item.icon,
15
+ size: '24'
16
+ } %}
14
17
  {% endif %}
15
18
  </a>
16
19
  </li>