@sebgroup/green-core 3.4.0 → 3.5.0

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.
@@ -6,12 +6,14 @@ import { GdsElement } from '../../gds-element';
6
6
  * When a user attempts to submit a form with errors, this component displays a summary of those errors.
7
7
  * Including an error summary greatly assists users in promptly identifying and addressing multiple errors
8
8
  * in a consolidated manner. It provides a clear indication of what went wrong and what needs to be corrected.
9
+ *
10
+ * @slot header - Optional slot for customizing the header of the summary.
9
11
  */
10
12
  export declare class GdsFormSummary extends GdsElement {
11
13
  #private;
12
14
  static styles: (import("lit").CSSResult | import("lit").CSSResult[])[];
13
15
  /**
14
- * Whether to hide the error messages under the labels.
16
+ * @deprecated This no longer has any effect, and will be removed in a future release.
15
17
  */
16
18
  hideErrors: boolean;
17
19
  /**
@@ -35,7 +37,7 @@ export declare class GdsFormSummary extends GdsElement {
35
37
  connectedCallback(): void;
36
38
  disconnectedCallback(): void;
37
39
  /**
38
- * Refresh the component to reflext the current state of the form.
40
+ * Refresh the component to reflect the current state of the form.
39
41
  */
40
42
  refresh(): void;
41
43
  render(): any;
@@ -5,7 +5,7 @@ import {
5
5
  __privateMethod,
6
6
  __privateSet
7
7
  } from "../../chunks/chunk.CAV4X6PU.js";
8
- var _form, _formObserver, _getFormControls, getFormControls_fn, _getErrorControls, getErrorControls_fn, _renderArrowIcon, renderArrowIcon_fn;
8
+ var _form, _formObserver, _renderControlItem, renderControlItem_fn, _groupByFieldset, groupByFieldset_fn, _getFormControls, getFormControls_fn, _getErrorControls, getErrorControls_fn, _renderArrowIcon, renderArrowIcon_fn;
9
9
  import { msg, str } from "@lit/localize";
10
10
  import { nothing } from "lit";
11
11
  import { property, queryAsync } from "lit/decorators.js";
@@ -14,15 +14,19 @@ import { when } from "lit/directives/when.js";
14
14
  import { GdsElement } from "../../gds-element.js";
15
15
  import { gdsCustomElement, html } from "../../scoping.js";
16
16
  import { tokens } from "../../tokens.style.js";
17
+ import { GdsAlert } from "../alert/alert.component.js";
17
18
  import { GdsButton } from "../button/button.component.js";
18
19
  import { GdsCard } from "../card/card.component.js";
19
20
  import { GdsDiv } from "../div/div.component.js";
20
21
  import { GdsFlex } from "../flex/flex.component.js";
21
22
  import { IconArrowUp } from "../icon/icons/arrow-up.component.js";
23
+ import { GdsText } from "../text/text.component.js";
22
24
  import SummaryStyles from "./summary.styles.js";
23
25
  let GdsFormSummary = class extends GdsElement {
24
26
  constructor() {
25
27
  super(...arguments);
28
+ __privateAdd(this, _renderControlItem);
29
+ __privateAdd(this, _groupByFieldset);
26
30
  __privateAdd(this, _getFormControls);
27
31
  __privateAdd(this, _getErrorControls);
28
32
  __privateAdd(this, _renderArrowIcon);
@@ -70,75 +74,62 @@ let GdsFormSummary = class extends GdsElement {
70
74
  __privateGet(this, _formObserver)?.disconnect();
71
75
  }
72
76
  /**
73
- * Refresh the component to reflext the current state of the form.
77
+ * Refresh the component to reflect the current state of the form.
74
78
  */
75
79
  refresh() {
76
80
  this.requestUpdate();
77
81
  }
78
82
  render() {
79
83
  const formControls = __privateMethod(this, _getFormControls, getFormControls_fn).call(this);
80
- const errorControls = __privateMethod(this, _getErrorControls, getErrorControls_fn).call(this);
84
+ const errorCount = __privateMethod(this, _getErrorControls, getErrorControls_fn).call(this).length;
85
+ const groupedControls = __privateMethod(this, _groupByFieldset, groupByFieldset_fn).call(this, formControls);
81
86
  return when(
82
- errorControls.length > 0,
87
+ errorCount > 0,
83
88
  () => html`<gds-card
84
89
  id="root"
85
90
  role="navigation"
86
- variant="negative"
87
- padding="l"
88
- background="negative-01"
89
- color="negative-01"
91
+ padding="xs"
92
+ background="neutral-02"
93
+ border-color="negative-01"
94
+ border-radius="m"
90
95
  overflow="hidden"
91
96
  aria-describedby="description"
92
97
  aria-label=${msg(`Form error summary`)}
93
98
  >
94
99
  <gds-flex gap="0" flex-direction="column">
95
- <gds-text font="heading-xs" font-weight="book" id="description">
96
- ${msg(
97
- str`There are ${errorControls.length} errors to correct before you can continue:`
100
+ <slot name="header">
101
+ <gds-alert
102
+ variant="negative"
103
+ label=${msg(
104
+ str`There are errors to correct before you can continue`
98
105
  )}
99
- </gds-text>
106
+ >
107
+ ${msg(str`There are errors to correct before you can continue`)}
108
+ </gds-alert>
109
+ </slot>
100
110
  <ul>
101
- ${formControls.map(
102
- (el, idx) => html`<li ?inert=${!(el.ariaInvalid === "true" || el.invalid)}>
103
- <gds-card
104
- display="flex"
105
- padding="s"
106
- flex-direction="row"
107
- align-items="center"
108
- justify-content="space-between"
109
- gap="xs"
110
- level="3"
111
- color="negative-01"
112
- background="transparent; hover: negative-01/.1"
113
- style="cursor: pointer"
114
- border-width="0"
115
- border-radius="xs"
116
- margin="0 -xs"
117
- @click=${(e) => {
118
- e.preventDefault();
119
- el.focus();
120
- }}
111
+ ${groupedControls.map(
112
+ (entry) => when(
113
+ entry.fieldset,
114
+ // Render fieldset groups with legends if they exist
115
+ () => html`<li
116
+ class="group"
117
+ ?inert=${!entry.controls.some(
118
+ (el) => el.ariaInvalid === "true" || el.invalid
119
+ )}
121
120
  >
122
- <div id=${`error-label-${idx}`}>
123
- <gds-div font-weight="book"
124
- >${el.dataset.label || el.label || el.ariaLabel}</gds-div
125
- >
126
- ${when(
127
- !this.hideErrors,
128
- () => html`<gds-div font="body-s-regular">
129
- ${el.dataset.errormessage || el.errorMessage || el.ariaErrorMessage}
130
- </gds-div>`
131
- )}
132
- </div>
133
- <gds-button
134
- size="small"
135
- variant="negative"
136
- label=${`Move focus to ${el.label} field`}
121
+ <gds-text tag="span" font="heading-s" margin="0 s xs"
122
+ >${entry.legend}</gds-text
137
123
  >
138
- ${until(__privateMethod(this, _renderArrowIcon, renderArrowIcon_fn).call(this, el), nothing)}
139
- </gds-button>
140
- </gds-card>
141
- </li>`
124
+ <ul>
125
+ ${entry.controls.map(
126
+ (el) => __privateMethod(this, _renderControlItem, renderControlItem_fn).call(this, el)
127
+ )}
128
+ </ul>
129
+ </li>`,
130
+ // Render standalone controls without a fieldset
131
+ () => entry.controls.map((el) => __privateMethod(this, _renderControlItem, renderControlItem_fn).call(this, el))
132
+ )
142
133
  )}
143
134
  </ul>
144
135
  </gds-flex>
@@ -148,12 +139,70 @@ let GdsFormSummary = class extends GdsElement {
148
139
  };
149
140
  _form = new WeakMap();
150
141
  _formObserver = new WeakMap();
142
+ _renderControlItem = new WeakSet();
143
+ renderControlItem_fn = function(el) {
144
+ return html`<li
145
+ class="item"
146
+ ?inert=${!(el.ariaInvalid === "true" || el.invalid)}
147
+ >
148
+ <gds-card
149
+ display="flex"
150
+ flex-direction="row"
151
+ align-items="center"
152
+ justify-content="space-between"
153
+ gap="xs"
154
+ level="3"
155
+ background="transparent; hover: neutral-02"
156
+ style="cursor: pointer"
157
+ border-width="0"
158
+ border-radius="xs"
159
+ padding="xs s"
160
+ @click=${(e) => {
161
+ e.preventDefault();
162
+ el.focus();
163
+ }}
164
+ >
165
+ <div>
166
+ <gds-div font-weight="book"
167
+ >${el.dataset.label || el.label || el.ariaLabel}</gds-div
168
+ >
169
+ </div>
170
+ <gds-button
171
+ size="xs"
172
+ rank="secondary"
173
+ label=${`Move focus to ${el.label} field`}
174
+ >
175
+ ${until(__privateMethod(this, _renderArrowIcon, renderArrowIcon_fn).call(this, el), nothing)}
176
+ </gds-button>
177
+ </gds-card>
178
+ </li>`;
179
+ };
180
+ _groupByFieldset = new WeakSet();
181
+ groupByFieldset_fn = function(controls) {
182
+ const groups = [];
183
+ for (const el of controls) {
184
+ const fieldset = el.closest("fieldset");
185
+ if (!fieldset) {
186
+ groups.push({ fieldset: null, legend: "", controls: [el] });
187
+ } else {
188
+ let group = groups.find((g) => g.fieldset === fieldset);
189
+ if (!group) {
190
+ const legend = fieldset.querySelector("legend")?.textContent?.trim() || "";
191
+ group = { fieldset, legend, controls: [] };
192
+ groups.push(group);
193
+ }
194
+ group.controls.push(el);
195
+ }
196
+ }
197
+ return groups;
198
+ };
151
199
  _getFormControls = new WeakSet();
152
200
  getFormControls_fn = function() {
153
201
  return Array.from(__privateGet(this, _form)?.elements || []).filter(
154
202
  // Individual checkboxes can be used as form controls, but they don't support error messages,
155
203
  // so we filter them out here. Checkboxes needs to be wrapped in a group to work with form summary.
156
- (el) => el.gdsElementName !== "gds-checkbox"
204
+ // Fieldsets are also listed in form.elements but are not actual controls.
205
+ (el) => el.gdsElementName !== "gds-checkbox" && el.tagName !== "FIELDSET"
157
206
  );
158
207
  };
159
208
  _getErrorControls = new WeakSet();
@@ -167,9 +216,13 @@ renderArrowIcon_fn = async function(el) {
167
216
  const selfTop = (await this._elRoot).getBoundingClientRect().top;
168
217
  const elTop = el.getBoundingClientRect().top;
169
218
  const isAbove = elTop < selfTop;
170
- return isAbove ? html`<gds-icon-arrow-up></gds-icon-arrow-up>` : html`<gds-icon-arrow-up
219
+ return when(
220
+ isAbove,
221
+ () => html`<gds-icon-arrow-up></gds-icon-arrow-up>`,
222
+ () => html`<gds-icon-arrow-up
171
223
  style="transform: rotate(180deg)"
172
- ></gds-icon-arrow-up>`;
224
+ ></gds-icon-arrow-up>`
225
+ );
173
226
  };
174
227
  GdsFormSummary.styles = [tokens, SummaryStyles];
175
228
  __decorateClass([
@@ -183,7 +236,15 @@ __decorateClass([
183
236
  ], GdsFormSummary.prototype, "_elRoot", 2);
184
237
  GdsFormSummary = __decorateClass([
185
238
  gdsCustomElement("gds-form-summary", {
186
- dependsOn: [GdsCard, GdsFlex, GdsDiv, GdsButton, IconArrowUp]
239
+ dependsOn: [
240
+ GdsCard,
241
+ GdsFlex,
242
+ GdsDiv,
243
+ GdsButton,
244
+ IconArrowUp,
245
+ GdsAlert,
246
+ GdsText
247
+ ]
187
248
  })
188
249
  ], GdsFormSummary);
189
250
  export {
@@ -9,6 +9,9 @@ var summary_styles_default = css`
9
9
  margin: 1rem 0 0;
10
10
  padding: 0;
11
11
  }
12
+ ul ul {
13
+ margin: 0;
14
+ }
12
15
  li {
13
16
  margin: 0;
14
17
  transition:
@@ -16,14 +19,25 @@ var summary_styles_default = css`
16
19
  opacity 0.3s ease-in-out,
17
20
  margin 0.3s ease-in-out;
18
21
  }
19
- li[inert] {
22
+ li.item[inert] {
20
23
  max-height: 0;
21
24
  opacity: 0;
22
25
  }
23
- li:not([inert]) {
26
+ li.item:not([inert]) {
24
27
  max-height: 4rem;
25
28
  opacity: 1;
26
29
  }
30
+ li.group[inert] {
31
+ max-height: 0;
32
+ opacity: 0;
33
+ overflow: hidden;
34
+ border-top-width: 0;
35
+ padding-top: 0;
36
+ margin-top: 0;
37
+ }
38
+ li.group:not([inert]) {
39
+ padding-top: var(--gds-sys-space-m);
40
+ }
27
41
  a {
28
42
  color: inherit;
29
43
  }