@brightspace-ui/core 3.116.5 → 3.117.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.
- package/components/calendar/README.md +1 -1
- package/components/calendar/calendar.js +0 -4
- package/components/inputs/demo/input-radio-label-test.js +1 -2
- package/components/inputs/demo/input-radio.html +63 -21
- package/components/inputs/input-radio-group.js +239 -0
- package/components/inputs/input-radio.js +177 -0
- package/custom-elements.json +182 -8
- package/package.json +1 -1
- package/components/inputs/demo/input-radio-label-simple-test.js +0 -25
- package/components/inputs/demo/input-radio-spacer-test.js +0 -42
@@ -32,7 +32,7 @@ Note: All `*-value` properties should be in ISO 8601 calendar date format (`YYYY
|
|
32
32
|
|
33
33
|
### Methods
|
34
34
|
|
35
|
-
- `
|
35
|
+
- `getShownYearAndMonth()`: returns an object containing the year and month in view.
|
36
36
|
|
37
37
|
## Accessibility
|
38
38
|
|
@@ -633,10 +633,6 @@ class Calendar extends LocalizeCoreElement(RtlMixin(LitElement)) {
|
|
633
633
|
}
|
634
634
|
}
|
635
635
|
|
636
|
-
getShownValue() {
|
637
|
-
return new Date(this._shownYear, this._shownMonth).toISOString();
|
638
|
-
}
|
639
|
-
|
640
636
|
getShownYearAndMonth() {
|
641
637
|
return { year: this._shownYear, month: this._shownMonth };
|
642
638
|
}
|
@@ -1,8 +1,7 @@
|
|
1
1
|
import { css, html, LitElement } from 'lit';
|
2
2
|
import { radioStyles } from '../input-radio-styles.js';
|
3
|
-
import { RtlMixin } from '../../../mixins/rtl/rtl-mixin.js';
|
4
3
|
|
5
|
-
class TestInputRadioLabel extends
|
4
|
+
class TestInputRadioLabel extends LitElement {
|
6
5
|
|
7
6
|
static get styles() {
|
8
7
|
return [ radioStyles,
|
@@ -6,11 +6,12 @@
|
|
6
6
|
<link rel="stylesheet" href="../../demo/styles.css" type="text/css">
|
7
7
|
<script type="module">
|
8
8
|
import '../../demo/demo-page.js';
|
9
|
-
import '
|
9
|
+
import '../../form/form.js';
|
10
|
+
import '../input-group.js';
|
11
|
+
import '../input-radio.js';
|
12
|
+
import '../input-radio-group.js';
|
13
|
+
import '../../inputs/input-text.js';
|
10
14
|
import './input-radio-label-test.js';
|
11
|
-
import './input-radio-label-simple-test.js';
|
12
|
-
import './input-radio-solo-test.js';
|
13
|
-
import './input-radio-spacer-test.js';
|
14
15
|
</script>
|
15
16
|
</head>
|
16
17
|
<body unresolved>
|
@@ -18,35 +19,76 @@
|
|
18
19
|
|
19
20
|
<h2>Simple radio group with labels</h2>
|
20
21
|
<d2l-demo-snippet>
|
21
|
-
<d2l-
|
22
|
+
<d2l-input-radio-group label="Bread">
|
23
|
+
<d2l-input-radio label="Whole wheat" value="whole-wheat"></d2l-input-radio>
|
24
|
+
<d2l-input-radio label="Baguette" value="baguette" checked></d2l-input-radio>
|
25
|
+
<d2l-input-radio label="Marble Rye" value="marble-rye"></d2l-input-radio>
|
26
|
+
</d2l-input-radio-group>
|
22
27
|
</d2l-demo-snippet>
|
23
28
|
|
24
|
-
<h2>
|
29
|
+
<h2>Required radio group in a form</h2>
|
25
30
|
<d2l-demo-snippet>
|
26
|
-
<d2l-
|
31
|
+
<d2l-form>
|
32
|
+
<d2l-input-group>
|
33
|
+
<d2l-input-radio-group name="bread" label="Bread" required>
|
34
|
+
<d2l-input-radio label="Whole wheat" value="whole-wheat"></d2l-input-radio>
|
35
|
+
<d2l-input-radio label="Baguette" value="baguette"></d2l-input-radio>
|
36
|
+
<d2l-input-radio label="Marble Rye" value="marble-rye"></d2l-input-radio>
|
37
|
+
</d2l-input-radio-group>
|
38
|
+
</d2l-input-group>
|
39
|
+
<button style="margin-top: 20px;">Save</button>
|
40
|
+
</d2l-form>
|
41
|
+
<script>
|
42
|
+
(demo => {
|
43
|
+
const form = demo.querySelector('d2l-form');
|
44
|
+
form.addEventListener('d2l-form-submit', (e) => {
|
45
|
+
console.log(`form submitted, value: "${e.detail.formData['bread']}"`);
|
46
|
+
});
|
47
|
+
const button = demo.querySelector('button');
|
48
|
+
button.addEventListener('click', () => form.submit());
|
49
|
+
})(document.currentScript.parentNode);
|
50
|
+
</script>
|
27
51
|
</d2l-demo-snippet>
|
28
52
|
|
29
|
-
<h2>
|
53
|
+
<h2>Group with disabled item</h2>
|
30
54
|
<d2l-demo-snippet>
|
31
|
-
<
|
32
|
-
<d2l-
|
33
|
-
<d2l-input-radio
|
34
|
-
|
35
|
-
|
36
|
-
</d2l-input-radio-spacer>
|
37
|
-
</template>
|
55
|
+
<d2l-input-radio-group label="Bread">
|
56
|
+
<d2l-input-radio label="Whole wheat" checked></d2l-input-radio>
|
57
|
+
<d2l-input-radio label="Baguette" disabled></d2l-input-radio>
|
58
|
+
<d2l-input-radio label="Marble Rye"></d2l-input-radio>
|
59
|
+
</d2l-input-radio-group>
|
38
60
|
</d2l-demo-snippet>
|
39
61
|
|
40
|
-
<h2>
|
62
|
+
<h2>Inline help</h2>
|
41
63
|
<d2l-demo-snippet>
|
42
|
-
<d2l-
|
43
|
-
|
44
|
-
|
64
|
+
<d2l-input-radio-group label="Bread">
|
65
|
+
<d2l-input-radio label="Whole wheat">
|
66
|
+
<div slot="inline-help">A healthy option, rich in fiber and nutrients.</div>
|
67
|
+
</d2l-input-radio>
|
68
|
+
<d2l-input-radio label="Baguette" checked>
|
69
|
+
<div slot="inline-help">A symbol of French culture and cuisine, with millions baked and consumed daily.</div>
|
70
|
+
</d2l-input-radio>
|
71
|
+
<d2l-input-radio label="Marble Rye">
|
72
|
+
<div slot="inline-help">Characterized by its distinctive appearance, achieved by weaving together light and dark rye dough.</div>
|
73
|
+
</d2l-input-radio>
|
74
|
+
</d2l-input-radio-group>
|
45
75
|
</d2l-demo-snippet>
|
46
76
|
|
47
|
-
<h2>
|
77
|
+
<h2>Supporting slot</h2>
|
48
78
|
<d2l-demo-snippet>
|
49
|
-
<d2l-
|
79
|
+
<d2l-input-radio-group label="Bread">
|
80
|
+
<d2l-input-radio label="Whole wheat"></d2l-input-radio>
|
81
|
+
<d2l-input-radio label="Baguette"></d2l-input-radio>
|
82
|
+
<d2l-input-radio label="Marble Rye"></d2l-input-radio>
|
83
|
+
<d2l-input-radio label="Other" checked supporting-hidden-when-unchecked>
|
84
|
+
<d2l-input-text label="Other" label-hidden value="Sourdough" slot="supporting"></d2l-input-text>
|
85
|
+
</d2l-input-radio>
|
86
|
+
</d2l-input-radio-group>
|
87
|
+
</d2l-demo-snippet>
|
88
|
+
|
89
|
+
<h2>Standard radios with styles</h2>
|
90
|
+
<d2l-demo-snippet>
|
91
|
+
<d2l-test-input-radio-label></d2l-test-input-radio-label>
|
50
92
|
</d2l-demo-snippet>
|
51
93
|
|
52
94
|
</d2l-demo-page>
|
@@ -0,0 +1,239 @@
|
|
1
|
+
import { css, html, LitElement } from 'lit';
|
2
|
+
import { FormElementMixin } from '../form/form-element-mixin.js';
|
3
|
+
import { getUniqueId } from '../../helpers/uniqueId.js';
|
4
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
5
|
+
import { inputLabelStyles } from './input-label-styles.js';
|
6
|
+
import { PropertyRequiredMixin } from '../../mixins/property-required/property-required-mixin.js';
|
7
|
+
import { SkeletonMixin } from '../skeleton/skeleton-mixin.js';
|
8
|
+
|
9
|
+
/**
|
10
|
+
* A group of <d2l-input-radio> components.
|
11
|
+
* @slot - Radio components
|
12
|
+
* @fires change - Dispatched when the radio group's state changes
|
13
|
+
*/
|
14
|
+
class InputRadioGroup extends PropertyRequiredMixin(SkeletonMixin(FormElementMixin(LitElement))) {
|
15
|
+
|
16
|
+
static get properties() {
|
17
|
+
return {
|
18
|
+
/**
|
19
|
+
* REQUIRED: Label for the input
|
20
|
+
* @type {string}
|
21
|
+
*/
|
22
|
+
label: { required: true, type: String },
|
23
|
+
/**
|
24
|
+
* Hides the label visually
|
25
|
+
* @type {boolean}
|
26
|
+
*/
|
27
|
+
labelHidden: { attribute: 'label-hidden', reflect: true, type: Boolean },
|
28
|
+
/**
|
29
|
+
* Indicates that a value is required
|
30
|
+
* @type {boolean}
|
31
|
+
*/
|
32
|
+
required: { type: Boolean, reflect: true }
|
33
|
+
};
|
34
|
+
}
|
35
|
+
|
36
|
+
static get styles() {
|
37
|
+
return [super.styles, inputLabelStyles, css`
|
38
|
+
:host {
|
39
|
+
display: block;
|
40
|
+
}
|
41
|
+
:host([hidden]) {
|
42
|
+
display: none;
|
43
|
+
}
|
44
|
+
div[role="radiogroup"] {
|
45
|
+
display: flex;
|
46
|
+
flex-direction: column;
|
47
|
+
gap: 0.6rem;
|
48
|
+
}
|
49
|
+
.d2l-input-label[hidden] {
|
50
|
+
display: none;
|
51
|
+
}
|
52
|
+
::slotted(:not(d2l-input-radio)) {
|
53
|
+
display: none;
|
54
|
+
}
|
55
|
+
`];
|
56
|
+
}
|
57
|
+
|
58
|
+
constructor() {
|
59
|
+
super();
|
60
|
+
this.labelHidden = false;
|
61
|
+
this.required = false;
|
62
|
+
this.setFormValue('');
|
63
|
+
}
|
64
|
+
|
65
|
+
render() {
|
66
|
+
return html`
|
67
|
+
<span class="d2l-input-label" ?hidden="${this.labelHidden}" id="${this.#labelId}"><span class="d2l-skeletize">${this.label}</span></span>
|
68
|
+
<div
|
69
|
+
aria-invalid="${ifDefined(this.invalid ? 'true' : undefined)}"
|
70
|
+
aria-labelledby="${this.#labelId}"
|
71
|
+
aria-required="${ifDefined(this.required ? 'true' : undefined)}"
|
72
|
+
@click="${this.#handleClick}"
|
73
|
+
@d2l-input-radio-checked="${this.#handleRadioChecked}"
|
74
|
+
@keydown="${this.#handleKeyDown}"
|
75
|
+
role="radiogroup">
|
76
|
+
<slot @slotchange="${this.#handleSlotChange}"></slot>
|
77
|
+
</div>
|
78
|
+
`;
|
79
|
+
}
|
80
|
+
|
81
|
+
willUpdate(changedProperties) {
|
82
|
+
super.willUpdate(changedProperties);
|
83
|
+
if (changedProperties.has('invalid')) {
|
84
|
+
const radios = this.#getRadios();
|
85
|
+
radios.forEach(el => el._invalid = this.invalid);
|
86
|
+
}
|
87
|
+
if (changedProperties.has('required')) {
|
88
|
+
this.#recalculateState(true);
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
focus() {
|
93
|
+
const radios = this.#getRadios();
|
94
|
+
if (radios.length === 0) return;
|
95
|
+
let firstFocusable = null;
|
96
|
+
let firstChecked = null;
|
97
|
+
radios.forEach(el => {
|
98
|
+
if (firstFocusable === null && !el.disabled) firstFocusable = el;
|
99
|
+
if (firstChecked === null && el._checked) firstChecked = el;
|
100
|
+
});
|
101
|
+
const focusElem = firstChecked || firstFocusable;
|
102
|
+
focusElem.focus();
|
103
|
+
setTimeout(() => focusElem.focus()); // timeout required when following link from form validation
|
104
|
+
}
|
105
|
+
|
106
|
+
#labelId = getUniqueId();
|
107
|
+
|
108
|
+
async #doUpdateChecked(newChecked, doFocus, doDispatchEvent) {
|
109
|
+
const radios = this.#getRadios();
|
110
|
+
let prevChecked = null;
|
111
|
+
radios.forEach(el => {
|
112
|
+
if (el._checked) prevChecked = el;
|
113
|
+
});
|
114
|
+
if (prevChecked === newChecked) return;
|
115
|
+
|
116
|
+
newChecked._checked = true;
|
117
|
+
if (prevChecked !== null) {
|
118
|
+
prevChecked._checked = false;
|
119
|
+
}
|
120
|
+
this.#recalculateState(true);
|
121
|
+
|
122
|
+
if (doDispatchEvent) {
|
123
|
+
this.dispatchEvent(new CustomEvent('change', {
|
124
|
+
bubbles: true,
|
125
|
+
composed: true,
|
126
|
+
detail: {
|
127
|
+
value: newChecked.value,
|
128
|
+
oldValue: prevChecked?.value
|
129
|
+
}
|
130
|
+
}));
|
131
|
+
}
|
132
|
+
|
133
|
+
if (doFocus) {
|
134
|
+
await newChecked.updateComplete; // wait for tabindex to be updated
|
135
|
+
newChecked.focus();
|
136
|
+
}
|
137
|
+
}
|
138
|
+
|
139
|
+
#getRadios() {
|
140
|
+
const elems = this.shadowRoot?.querySelector('slot')?.assignedElements();
|
141
|
+
if (!elems) return [];
|
142
|
+
return elems.filter(el => el.tagName === 'D2L-INPUT-RADIO');
|
143
|
+
}
|
144
|
+
|
145
|
+
#handleClick(e) {
|
146
|
+
if (e.target.tagName !== 'D2L-INPUT-RADIO') return;
|
147
|
+
if (e.target.disabled) return;
|
148
|
+
this.#doUpdateChecked(e.target, true, true);
|
149
|
+
e.preventDefault();
|
150
|
+
}
|
151
|
+
|
152
|
+
#handleKeyDown(e) {
|
153
|
+
if (e.target.tagName !== 'D2L-INPUT-RADIO') return;
|
154
|
+
|
155
|
+
const isRtl = (getComputedStyle(this).direction === 'rtl');
|
156
|
+
let newOffset = null;
|
157
|
+
if (e.key === ' ') {
|
158
|
+
newOffset = 0;
|
159
|
+
} else if (e.key === 'ArrowUp' || (!isRtl && e.key === 'ArrowLeft') || (isRtl && e.key === 'ArrowRight')) {
|
160
|
+
newOffset = -1;
|
161
|
+
} else if (e.key === 'ArrowDown' || (!isRtl && e.key === 'ArrowRight') || (isRtl && e.key === 'ArrowLeft')) {
|
162
|
+
newOffset = 1;
|
163
|
+
}
|
164
|
+
|
165
|
+
if (newOffset === null) return;
|
166
|
+
|
167
|
+
const radios = this.#getRadios().filter(el => !el.disabled || el._checked);
|
168
|
+
let checkedIndex = -1;
|
169
|
+
let firstFocusableIndex = -1;
|
170
|
+
radios.forEach((el, i) => {
|
171
|
+
if (el._checked) checkedIndex = i;
|
172
|
+
if (firstFocusableIndex < 0 && !el.disabled) firstFocusableIndex = i;
|
173
|
+
});
|
174
|
+
if (checkedIndex === -1) {
|
175
|
+
if (firstFocusableIndex === -1) return;
|
176
|
+
checkedIndex = firstFocusableIndex;
|
177
|
+
}
|
178
|
+
|
179
|
+
const newIndex = (checkedIndex + newOffset + radios.length) % radios.length;
|
180
|
+
this.#doUpdateChecked(radios[newIndex], true, true);
|
181
|
+
|
182
|
+
e.preventDefault();
|
183
|
+
}
|
184
|
+
|
185
|
+
#handleRadioChecked(e) {
|
186
|
+
if (e.detail.checked) {
|
187
|
+
this.#doUpdateChecked(e.target, false, false);
|
188
|
+
} else {
|
189
|
+
e.target._checked = false;
|
190
|
+
this.#recalculateState(true);
|
191
|
+
}
|
192
|
+
}
|
193
|
+
|
194
|
+
#handleSlotChange() {
|
195
|
+
this.#recalculateState(false);
|
196
|
+
}
|
197
|
+
|
198
|
+
#recalculateState(doValidate) {
|
199
|
+
const radios = this.#getRadios();
|
200
|
+
if (radios.length === 0) return;
|
201
|
+
|
202
|
+
let firstFocusable = null;
|
203
|
+
const checkedRadios = [];
|
204
|
+
radios.forEach(el => {
|
205
|
+
if (firstFocusable === null && !el.disabled) firstFocusable = el;
|
206
|
+
if (el._checked) checkedRadios.push(el);
|
207
|
+
el._isInitFromGroup = true;
|
208
|
+
el._firstFocusable = false;
|
209
|
+
});
|
210
|
+
|
211
|
+
// let the first non-disabled radio know it's first so it can be focusable
|
212
|
+
if (checkedRadios.length === 0 && firstFocusable !== null) {
|
213
|
+
firstFocusable._firstFocusable = true;
|
214
|
+
}
|
215
|
+
|
216
|
+
// only the last checked radio is actually checked
|
217
|
+
for (let i = 0; i < checkedRadios.length - 1; i++) {
|
218
|
+
checkedRadios[i]._checked = false;
|
219
|
+
}
|
220
|
+
if (checkedRadios.length > 0) {
|
221
|
+
const lastCheckedRadio = checkedRadios[checkedRadios.length - 1];
|
222
|
+
lastCheckedRadio._checked = true;
|
223
|
+
this.setFormValue(lastCheckedRadio.value);
|
224
|
+
if (this.required) {
|
225
|
+
this.setValidity({ valueMissing: false });
|
226
|
+
}
|
227
|
+
} else {
|
228
|
+
this.setFormValue('');
|
229
|
+
if (this.required) {
|
230
|
+
this.setValidity({ valueMissing: true });
|
231
|
+
}
|
232
|
+
}
|
233
|
+
if (doValidate && this.required) {
|
234
|
+
this.requestValidate(true);
|
235
|
+
}
|
236
|
+
}
|
237
|
+
|
238
|
+
}
|
239
|
+
customElements.define('d2l-input-radio-group', InputRadioGroup);
|
@@ -0,0 +1,177 @@
|
|
1
|
+
import { css, html, LitElement, nothing } from 'lit';
|
2
|
+
import { classMap } from 'lit/directives/class-map.js';
|
3
|
+
import { FocusMixin } from '../../mixins/focus/focus-mixin.js';
|
4
|
+
import { getUniqueId } from '../../helpers/uniqueId.js';
|
5
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
6
|
+
import { InputInlineHelpMixin } from './input-inline-help.js';
|
7
|
+
import { PropertyRequiredMixin } from '../../mixins/property-required/property-required-mixin.js';
|
8
|
+
import { radioStyles } from './input-radio-styles.js';
|
9
|
+
import { SkeletonMixin } from '../skeleton/skeleton-mixin.js';
|
10
|
+
|
11
|
+
/**
|
12
|
+
* A radio input within a <d2l-input-radio-group>.
|
13
|
+
* @slot inline-help - Help text that will appear below the input. Use this only when other helpful cues are not sufficient, such as a carefully-worded label.
|
14
|
+
* @slot supporting - Supporting information which will appear below and be aligned with the input.
|
15
|
+
*/
|
16
|
+
class InputRadio extends InputInlineHelpMixin(SkeletonMixin(FocusMixin(PropertyRequiredMixin(LitElement)))) {
|
17
|
+
|
18
|
+
static get properties() {
|
19
|
+
return {
|
20
|
+
/**
|
21
|
+
* Checked state
|
22
|
+
* @type {boolean}
|
23
|
+
*/
|
24
|
+
checked: { type: Boolean, reflect: true },
|
25
|
+
/**
|
26
|
+
* ACCESSIBILITY: Additional information communicated to screenreader users when focusing on the input
|
27
|
+
* @type {string}
|
28
|
+
*/
|
29
|
+
description: { type: String },
|
30
|
+
/**
|
31
|
+
* Disables the input
|
32
|
+
* @type {boolean}
|
33
|
+
*/
|
34
|
+
disabled: { type: Boolean, reflect: true },
|
35
|
+
/**
|
36
|
+
* REQUIRED: Label for the input
|
37
|
+
* @type {string}
|
38
|
+
*/
|
39
|
+
label: { required: true, type: String },
|
40
|
+
/**
|
41
|
+
* Hides the supporting slot when unchecked
|
42
|
+
* @type {boolean}
|
43
|
+
*/
|
44
|
+
supportingHiddenWhenUnchecked: { type: Boolean, attribute: 'supporting-hidden-when-unchecked', reflect: true },
|
45
|
+
/**
|
46
|
+
* Value of the input
|
47
|
+
* @type {string}
|
48
|
+
*/
|
49
|
+
value: { type: String },
|
50
|
+
_checked: { state: true },
|
51
|
+
_firstFocusable: { state: true },
|
52
|
+
_hasSupporting: { state: true },
|
53
|
+
_isHovered: { state: true },
|
54
|
+
_invalid: { state: true }
|
55
|
+
};
|
56
|
+
}
|
57
|
+
|
58
|
+
static get styles() {
|
59
|
+
return [super.styles, radioStyles, css`
|
60
|
+
:host {
|
61
|
+
display: block;
|
62
|
+
}
|
63
|
+
:host([hidden]) {
|
64
|
+
display: none;
|
65
|
+
}
|
66
|
+
.d2l-input-radio-label {
|
67
|
+
cursor: default;
|
68
|
+
margin-block-end: 0;
|
69
|
+
}
|
70
|
+
.d2l-input-inline-help,
|
71
|
+
.d2l-input-radio-supporting {
|
72
|
+
margin-inline-start: 1.7rem;
|
73
|
+
}
|
74
|
+
.d2l-input-radio-supporting {
|
75
|
+
display: none;
|
76
|
+
margin-block-start: 0.6rem;
|
77
|
+
}
|
78
|
+
.d2l-input-radio-supporting-visible {
|
79
|
+
display: block;
|
80
|
+
}
|
81
|
+
`];
|
82
|
+
}
|
83
|
+
|
84
|
+
constructor() {
|
85
|
+
super();
|
86
|
+
this.disabled = false;
|
87
|
+
this.supportingHiddenWhenUnchecked = false;
|
88
|
+
this.value = 'on';
|
89
|
+
this._checked = false;
|
90
|
+
this._firstFocusable = false;
|
91
|
+
this._hasSupporting = false;
|
92
|
+
this._isHovered = false;
|
93
|
+
this._isInitFromGroup = false;
|
94
|
+
this._invalid = false;
|
95
|
+
}
|
96
|
+
|
97
|
+
get checked() { return this._checked; }
|
98
|
+
set checked(value) {
|
99
|
+
if (value === this._checked) return;
|
100
|
+
if (!this._isInitFromGroup) {
|
101
|
+
this._checked = value;
|
102
|
+
} else {
|
103
|
+
/** @ignore */
|
104
|
+
this.dispatchEvent(
|
105
|
+
new CustomEvent(
|
106
|
+
'd2l-input-radio-checked',
|
107
|
+
{ bubbles: true, composed: false, detail: { checked: value } }
|
108
|
+
)
|
109
|
+
);
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
static get focusElementSelector() {
|
114
|
+
return '.d2l-input-radio';
|
115
|
+
}
|
116
|
+
|
117
|
+
render() {
|
118
|
+
const disabled = this.disabled || this.skeleton;
|
119
|
+
const labelClasses = {
|
120
|
+
'd2l-input-radio-label': true,
|
121
|
+
'd2l-input-radio-label-disabled': this.disabled && !this.skeleton,
|
122
|
+
};
|
123
|
+
const radioClasses = {
|
124
|
+
'd2l-input-radio': true,
|
125
|
+
'd2l-disabled': this.disabled && !this.skeleton,
|
126
|
+
'd2l-hovering': this._isHovered && !disabled,
|
127
|
+
'd2l-skeletize': true
|
128
|
+
};
|
129
|
+
const supportingClasses = {
|
130
|
+
'd2l-input-radio-supporting': true,
|
131
|
+
'd2l-input-radio-supporting-visible': this._hasSupporting && (!this.supportingHiddenWhenUnchecked || this._checked),
|
132
|
+
};
|
133
|
+
const description = this.description ? html`<div id="${this.#descriptionId}" hidden>${this.description}</div>` : nothing;
|
134
|
+
const ariaDescribedByIds = `${this.description ? this.#descriptionId : ''} ${this._hasInlineHelp ? this.#inlineHelpId : ''}`.trim();
|
135
|
+
const tabindex = (!disabled && (this._checked || this._firstFocusable)) ? '0' : undefined;
|
136
|
+
return html`
|
137
|
+
<div class="${classMap(labelClasses)}" @mouseover="${this.#handleMouseOver}" @mouseout="${this.#handleMouseOut}">
|
138
|
+
<div
|
139
|
+
aria-checked="${this._checked}"
|
140
|
+
aria-describedby="${ifDefined(ariaDescribedByIds.length > 0 ? ariaDescribedByIds : undefined)}"
|
141
|
+
aria-disabled="${ifDefined(disabled ? 'true' : undefined)}"
|
142
|
+
aria-invalid="${ifDefined(this._invalid ? 'true' : undefined)}"
|
143
|
+
aria-labelledby="${this.#labelId}"
|
144
|
+
class="${classMap(radioClasses)}"
|
145
|
+
role="radio"
|
146
|
+
tabindex="${ifDefined(tabindex)}"></div>
|
147
|
+
<div id="${this.#labelId}" class="d2l-skeletize">${this.label}</div>
|
148
|
+
</div>
|
149
|
+
${this._renderInlineHelp(this.#inlineHelpId)}
|
150
|
+
${description}
|
151
|
+
<div class="${classMap(supportingClasses)}" @change="${this.#handleSupportingChange}"><slot name="supporting" @slotchange="${this.#handleSupportingSlotChange}"></slot></div>
|
152
|
+
`;
|
153
|
+
}
|
154
|
+
|
155
|
+
#descriptionId = getUniqueId();
|
156
|
+
#inlineHelpId = getUniqueId();
|
157
|
+
#labelId = getUniqueId();
|
158
|
+
|
159
|
+
#handleMouseOut() {
|
160
|
+
this._isHovered = false;
|
161
|
+
}
|
162
|
+
|
163
|
+
#handleMouseOver() {
|
164
|
+
this._isHovered = true;
|
165
|
+
}
|
166
|
+
|
167
|
+
#handleSupportingChange(e) {
|
168
|
+
e.stopPropagation();
|
169
|
+
}
|
170
|
+
|
171
|
+
#handleSupportingSlotChange(e) {
|
172
|
+
const content = e.target.assignedNodes({ flatten: true });
|
173
|
+
this._hasSupporting = content?.length > 0;
|
174
|
+
}
|
175
|
+
|
176
|
+
}
|
177
|
+
customElements.define('d2l-input-radio', InputRadio);
|
package/custom-elements.json
CHANGED
@@ -5171,10 +5171,6 @@
|
|
5171
5171
|
}
|
5172
5172
|
]
|
5173
5173
|
},
|
5174
|
-
{
|
5175
|
-
"name": "d2l-test-input-radio-label-simple",
|
5176
|
-
"path": "./components/inputs/demo/input-radio-label-simple-test.js"
|
5177
|
-
},
|
5178
5174
|
{
|
5179
5175
|
"name": "d2l-test-input-radio-label",
|
5180
5176
|
"path": "./components/inputs/demo/input-radio-label-test.js"
|
@@ -5220,10 +5216,6 @@
|
|
5220
5216
|
}
|
5221
5217
|
]
|
5222
5218
|
},
|
5223
|
-
{
|
5224
|
-
"name": "d2l-test-input-radio-spacer",
|
5225
|
-
"path": "./components/inputs/demo/input-radio-spacer-test.js"
|
5226
|
-
},
|
5227
5219
|
{
|
5228
5220
|
"name": "d2l-test-input-select",
|
5229
5221
|
"path": "./components/inputs/demo/input-select-test.js",
|
@@ -7017,6 +7009,86 @@
|
|
7017
7009
|
}
|
7018
7010
|
]
|
7019
7011
|
},
|
7012
|
+
{
|
7013
|
+
"name": "d2l-input-radio-group",
|
7014
|
+
"path": "./components/inputs/input-radio-group.js",
|
7015
|
+
"description": "A group of <d2l-input-radio> components.",
|
7016
|
+
"attributes": [
|
7017
|
+
{
|
7018
|
+
"name": "label",
|
7019
|
+
"description": "REQUIRED: Label for the input",
|
7020
|
+
"type": "string"
|
7021
|
+
},
|
7022
|
+
{
|
7023
|
+
"name": "label-hidden",
|
7024
|
+
"description": "Hides the label visually",
|
7025
|
+
"type": "boolean",
|
7026
|
+
"default": "false"
|
7027
|
+
},
|
7028
|
+
{
|
7029
|
+
"name": "required",
|
7030
|
+
"description": "Indicates that a value is required",
|
7031
|
+
"type": "boolean",
|
7032
|
+
"default": "false"
|
7033
|
+
},
|
7034
|
+
{
|
7035
|
+
"name": "skeleton",
|
7036
|
+
"description": "Render the component as a [skeleton loader](https://github.com/BrightspaceUI/core/tree/main/components/skeleton).",
|
7037
|
+
"type": "boolean"
|
7038
|
+
},
|
7039
|
+
{
|
7040
|
+
"name": "name",
|
7041
|
+
"description": "Name of the form control. Submitted with the form as part of a name/value pair.",
|
7042
|
+
"type": "string"
|
7043
|
+
}
|
7044
|
+
],
|
7045
|
+
"properties": [
|
7046
|
+
{
|
7047
|
+
"name": "label",
|
7048
|
+
"attribute": "label",
|
7049
|
+
"description": "REQUIRED: Label for the input",
|
7050
|
+
"type": "string"
|
7051
|
+
},
|
7052
|
+
{
|
7053
|
+
"name": "labelHidden",
|
7054
|
+
"attribute": "label-hidden",
|
7055
|
+
"description": "Hides the label visually",
|
7056
|
+
"type": "boolean",
|
7057
|
+
"default": "false"
|
7058
|
+
},
|
7059
|
+
{
|
7060
|
+
"name": "required",
|
7061
|
+
"attribute": "required",
|
7062
|
+
"description": "Indicates that a value is required",
|
7063
|
+
"type": "boolean",
|
7064
|
+
"default": "false"
|
7065
|
+
},
|
7066
|
+
{
|
7067
|
+
"name": "skeleton",
|
7068
|
+
"attribute": "skeleton",
|
7069
|
+
"description": "Render the component as a [skeleton loader](https://github.com/BrightspaceUI/core/tree/main/components/skeleton).",
|
7070
|
+
"type": "boolean"
|
7071
|
+
},
|
7072
|
+
{
|
7073
|
+
"name": "name",
|
7074
|
+
"attribute": "name",
|
7075
|
+
"description": "Name of the form control. Submitted with the form as part of a name/value pair.",
|
7076
|
+
"type": "string"
|
7077
|
+
}
|
7078
|
+
],
|
7079
|
+
"events": [
|
7080
|
+
{
|
7081
|
+
"name": "change",
|
7082
|
+
"description": "Dispatched when the radio group's state changes"
|
7083
|
+
}
|
7084
|
+
],
|
7085
|
+
"slots": [
|
7086
|
+
{
|
7087
|
+
"name": "",
|
7088
|
+
"description": "Radio components"
|
7089
|
+
}
|
7090
|
+
]
|
7091
|
+
},
|
7020
7092
|
{
|
7021
7093
|
"name": "d2l-input-radio-spacer",
|
7022
7094
|
"path": "./components/inputs/input-radio-spacer.js",
|
@@ -7028,6 +7100,108 @@
|
|
7028
7100
|
}
|
7029
7101
|
]
|
7030
7102
|
},
|
7103
|
+
{
|
7104
|
+
"name": "d2l-input-radio",
|
7105
|
+
"path": "./components/inputs/input-radio.js",
|
7106
|
+
"description": "A radio input within a <d2l-input-radio-group>.",
|
7107
|
+
"attributes": [
|
7108
|
+
{
|
7109
|
+
"name": "description",
|
7110
|
+
"description": "ACCESSIBILITY: Additional information communicated to screenreader users when focusing on the input",
|
7111
|
+
"type": "string"
|
7112
|
+
},
|
7113
|
+
{
|
7114
|
+
"name": "label",
|
7115
|
+
"description": "REQUIRED: Label for the input",
|
7116
|
+
"type": "string"
|
7117
|
+
},
|
7118
|
+
{
|
7119
|
+
"name": "checked",
|
7120
|
+
"description": "Checked state",
|
7121
|
+
"type": "boolean"
|
7122
|
+
},
|
7123
|
+
{
|
7124
|
+
"name": "disabled",
|
7125
|
+
"description": "Disables the input",
|
7126
|
+
"type": "boolean",
|
7127
|
+
"default": "false"
|
7128
|
+
},
|
7129
|
+
{
|
7130
|
+
"name": "supporting-hidden-when-unchecked",
|
7131
|
+
"description": "Hides the supporting slot when unchecked",
|
7132
|
+
"type": "boolean",
|
7133
|
+
"default": "false"
|
7134
|
+
},
|
7135
|
+
{
|
7136
|
+
"name": "value",
|
7137
|
+
"description": "Value of the input",
|
7138
|
+
"type": "string",
|
7139
|
+
"default": "\"on\""
|
7140
|
+
},
|
7141
|
+
{
|
7142
|
+
"name": "skeleton",
|
7143
|
+
"description": "Render the component as a [skeleton loader](https://github.com/BrightspaceUI/core/tree/main/components/skeleton).",
|
7144
|
+
"type": "boolean"
|
7145
|
+
}
|
7146
|
+
],
|
7147
|
+
"properties": [
|
7148
|
+
{
|
7149
|
+
"name": "description",
|
7150
|
+
"attribute": "description",
|
7151
|
+
"description": "ACCESSIBILITY: Additional information communicated to screenreader users when focusing on the input",
|
7152
|
+
"type": "string"
|
7153
|
+
},
|
7154
|
+
{
|
7155
|
+
"name": "label",
|
7156
|
+
"attribute": "label",
|
7157
|
+
"description": "REQUIRED: Label for the input",
|
7158
|
+
"type": "string"
|
7159
|
+
},
|
7160
|
+
{
|
7161
|
+
"name": "checked",
|
7162
|
+
"attribute": "checked",
|
7163
|
+
"description": "Checked state",
|
7164
|
+
"type": "boolean"
|
7165
|
+
},
|
7166
|
+
{
|
7167
|
+
"name": "disabled",
|
7168
|
+
"attribute": "disabled",
|
7169
|
+
"description": "Disables the input",
|
7170
|
+
"type": "boolean",
|
7171
|
+
"default": "false"
|
7172
|
+
},
|
7173
|
+
{
|
7174
|
+
"name": "supportingHiddenWhenUnchecked",
|
7175
|
+
"attribute": "supporting-hidden-when-unchecked",
|
7176
|
+
"description": "Hides the supporting slot when unchecked",
|
7177
|
+
"type": "boolean",
|
7178
|
+
"default": "false"
|
7179
|
+
},
|
7180
|
+
{
|
7181
|
+
"name": "value",
|
7182
|
+
"attribute": "value",
|
7183
|
+
"description": "Value of the input",
|
7184
|
+
"type": "string",
|
7185
|
+
"default": "\"on\""
|
7186
|
+
},
|
7187
|
+
{
|
7188
|
+
"name": "skeleton",
|
7189
|
+
"attribute": "skeleton",
|
7190
|
+
"description": "Render the component as a [skeleton loader](https://github.com/BrightspaceUI/core/tree/main/components/skeleton).",
|
7191
|
+
"type": "boolean"
|
7192
|
+
}
|
7193
|
+
],
|
7194
|
+
"slots": [
|
7195
|
+
{
|
7196
|
+
"name": "inline-help",
|
7197
|
+
"description": "Help text that will appear below the input. Use this only when other helpful cues are not sufficient, such as a carefully-worded label."
|
7198
|
+
},
|
7199
|
+
{
|
7200
|
+
"name": "supporting",
|
7201
|
+
"description": "Supporting information which will appear below and be aligned with the input."
|
7202
|
+
}
|
7203
|
+
]
|
7204
|
+
},
|
7031
7205
|
{
|
7032
7206
|
"name": "d2l-input-search",
|
7033
7207
|
"path": "./components/inputs/input-search.js",
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@brightspace-ui/core",
|
3
|
-
"version": "3.
|
3
|
+
"version": "3.117.0",
|
4
4
|
"description": "A collection of accessible, free, open-source web components for building Brightspace applications",
|
5
5
|
"type": "module",
|
6
6
|
"repository": "https://github.com/BrightspaceUI/core.git",
|
@@ -1,25 +0,0 @@
|
|
1
|
-
import { html, LitElement } from 'lit';
|
2
|
-
import { radioStyles } from '../input-radio-styles.js';
|
3
|
-
import { RtlMixin } from '../../../mixins/rtl/rtl-mixin.js';
|
4
|
-
|
5
|
-
class TestInputRadioLabelSimple extends RtlMixin(LitElement) {
|
6
|
-
|
7
|
-
static get styles() {
|
8
|
-
return [ radioStyles ];
|
9
|
-
}
|
10
|
-
|
11
|
-
render() {
|
12
|
-
return html`
|
13
|
-
<label class="d2l-input-radio-label">
|
14
|
-
<input type="radio" name="myGroup" value="first" checked>
|
15
|
-
Option 1
|
16
|
-
</label>
|
17
|
-
<label class="d2l-input-radio-label">
|
18
|
-
<input type="radio" name="myGroup" value="second">
|
19
|
-
Option 2
|
20
|
-
</label>
|
21
|
-
`;
|
22
|
-
}
|
23
|
-
|
24
|
-
}
|
25
|
-
customElements.define('d2l-test-input-radio-label-simple', TestInputRadioLabelSimple);
|
@@ -1,42 +0,0 @@
|
|
1
|
-
import '../input-radio-spacer.js';
|
2
|
-
import { html, LitElement } from 'lit';
|
3
|
-
import { inlineHelpStyles } from '../input-inline-help.js';
|
4
|
-
import { radioStyles } from '../input-radio-styles.js';
|
5
|
-
|
6
|
-
class TestInputRadioSpacer extends LitElement {
|
7
|
-
|
8
|
-
static get styles() {
|
9
|
-
return [ radioStyles, inlineHelpStyles ];
|
10
|
-
}
|
11
|
-
|
12
|
-
render() {
|
13
|
-
return html`
|
14
|
-
<div>
|
15
|
-
<label class="d2l-input-radio-label">
|
16
|
-
<input type="radio" aria-describedby="desc1" name="myGroup" value="normal">
|
17
|
-
Option 1
|
18
|
-
</label>
|
19
|
-
<d2l-input-radio-spacer id="desc1" class="d2l-input-inline-help">
|
20
|
-
Additional content can go here and will line up nicely with the edge of the radio.
|
21
|
-
</d2l-input-radio-spacer>
|
22
|
-
</div>
|
23
|
-
<div>
|
24
|
-
<label class="d2l-input-radio-label">
|
25
|
-
<input type="radio" aria-describedby="desc2" name="myGroup" value="normal">
|
26
|
-
Option 1 (Deadlights jack lad schooner scallywag dance the hempen jig carouser broadside cable strike colors. Bring a spring upon her cable holystone blow the man down spanker.)
|
27
|
-
</label>
|
28
|
-
<d2l-input-radio-spacer id="desc2" class="d2l-input-inline-help">
|
29
|
-
Trysail Sail ho Corsair red ensign hulk smartly boom jib rum gangway. Case shot Shiver me timbers gangplank crack Jennys tea cup ballast Blimey lee snow crow's nest rutters. Fluke jib scourge of the seven seas boatswain schooner gaff booty Jack Tar transom spirits.
|
30
|
-
</d2l-input-radio-spacer>
|
31
|
-
</div>
|
32
|
-
`;
|
33
|
-
}
|
34
|
-
|
35
|
-
focus() {
|
36
|
-
const elem = this.shadowRoot && this.shadowRoot.querySelector('input');
|
37
|
-
if (elem) elem.focus();
|
38
|
-
}
|
39
|
-
|
40
|
-
}
|
41
|
-
|
42
|
-
customElements.define('d2l-test-input-radio-spacer', TestInputRadioSpacer);
|