@crowdstrike/glide-core 0.9.1 → 0.9.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.
Files changed (109) hide show
  1. package/dist/accordion.styles.js +2 -4
  2. package/dist/button-group.button.styles.js +3 -8
  3. package/dist/button-group.styles.js +2 -2
  4. package/dist/button.d.ts +1 -0
  5. package/dist/button.js +1 -1
  6. package/dist/button.styles.js +2 -4
  7. package/dist/button.test.events.js +86 -10
  8. package/dist/checkbox.js +1 -1
  9. package/dist/checkbox.styles.js +43 -6
  10. package/dist/checkbox.test.form.js +16 -0
  11. package/dist/checkbox.test.interactions.js +8 -0
  12. package/dist/drawer.js +1 -1
  13. package/dist/dropdown.d.ts +4 -2
  14. package/dist/dropdown.js +1 -1
  15. package/dist/dropdown.option.js +1 -1
  16. package/dist/dropdown.option.styles.js +1 -0
  17. package/dist/dropdown.styles.js +47 -26
  18. package/dist/dropdown.test.focus.filterable.js +20 -0
  19. package/dist/dropdown.test.focus.js +1 -0
  20. package/dist/dropdown.test.form.js +23 -112
  21. package/dist/dropdown.test.interactions.filterable.js +121 -17
  22. package/dist/dropdown.test.interactions.multiple.js +15 -22
  23. package/dist/dropdown.test.interactions.single.js +44 -22
  24. package/dist/icon-button.styles.js +2 -4
  25. package/dist/icons/checked.d.ts +5 -0
  26. package/dist/icons/checked.js +1 -1
  27. package/dist/input.d.ts +1 -1
  28. package/dist/input.js +1 -1
  29. package/dist/input.stories.d.ts +0 -4
  30. package/dist/input.styles.d.ts +1 -1
  31. package/dist/input.styles.js +93 -93
  32. package/dist/input.test.basics.js +45 -45
  33. package/dist/input.test.form.js +17 -0
  34. package/dist/label.styles.js +6 -11
  35. package/dist/library/localize.test.js +45 -0
  36. package/dist/menu.button.styles.js +1 -0
  37. package/dist/menu.js +1 -1
  38. package/dist/menu.link.styles.js +1 -0
  39. package/dist/menu.styles.js +3 -1
  40. package/dist/menu.test.events.js +1 -97
  41. package/dist/menu.test.focus.js +26 -3
  42. package/dist/menu.test.interactions.js +3 -0
  43. package/dist/modal.d.ts +0 -7
  44. package/dist/modal.icon-button.test.basics.js +9 -9
  45. package/dist/modal.styles.js +2 -4
  46. package/dist/modal.tertiary-icon.test.basics.js +14 -14
  47. package/dist/modal.test.accessibility.js +16 -27
  48. package/dist/modal.test.basics.js +64 -68
  49. package/dist/modal.test.close.js +12 -16
  50. package/dist/modal.test.events.js +32 -44
  51. package/dist/modal.test.lock-scroll.js +15 -25
  52. package/dist/modal.test.methods.js +8 -12
  53. package/dist/modal.test.scrollbars.js +2 -4
  54. package/dist/radio-group.js +1 -1
  55. package/dist/radio-group.test.basics.js +3 -3
  56. package/dist/radio-group.test.events.js +6 -6
  57. package/dist/radio-group.test.form.js +19 -0
  58. package/dist/radio.styles.js +2 -6
  59. package/dist/split-button.styles.js +2 -4
  60. package/dist/split-container.styles.js +2 -4
  61. package/dist/styles/focus-outline.d.ts +1 -1
  62. package/dist/styles/focus-outline.js +7 -1
  63. package/dist/styles/menu-opening-animation.d.ts +2 -0
  64. package/dist/styles/menu-opening-animation.js +26 -0
  65. package/dist/styles/variables.css +1 -1
  66. package/dist/styles/visually-hidden.d.ts +1 -1
  67. package/dist/styles/visually-hidden.js +14 -1
  68. package/dist/tab.group.d.ts +6 -6
  69. package/dist/tab.group.js +1 -1
  70. package/dist/tab.group.styles.js +46 -5
  71. package/dist/tab.group.test.basics.js +9 -2
  72. package/dist/tab.group.test.interactions.js +70 -93
  73. package/dist/tab.js +1 -1
  74. package/dist/tab.panel.styles.js +3 -9
  75. package/dist/tab.styles.js +6 -13
  76. package/dist/tab.test.basics.js +15 -17
  77. package/dist/tag.js +1 -1
  78. package/dist/tag.styles.js +2 -4
  79. package/dist/tag.test.basics.js +28 -27
  80. package/dist/tag.test.events.js +3 -3
  81. package/dist/tag.test.focus.js +4 -4
  82. package/dist/textarea.d.ts +1 -1
  83. package/dist/textarea.stories.d.ts +0 -4
  84. package/dist/textarea.styles.d.ts +1 -1
  85. package/dist/textarea.styles.js +63 -67
  86. package/dist/textarea.test.basics.js +52 -52
  87. package/dist/toasts.d.ts +5 -0
  88. package/dist/toasts.styles.js +1 -1
  89. package/dist/toggle.styles.js +2 -1
  90. package/dist/toggle.test.interactions.js +11 -0
  91. package/dist/tooltip.js +1 -1
  92. package/dist/tooltip.styles.js +22 -18
  93. package/dist/tooltip.test.interactions.js +6 -6
  94. package/dist/translations/en.js +1 -1
  95. package/dist/translations/fr.d.ts +3 -1
  96. package/dist/translations/fr.js +1 -1
  97. package/dist/translations/ja.d.ts +3 -1
  98. package/dist/translations/ja.js +1 -1
  99. package/dist/tree.item.styles.js +11 -3
  100. package/dist/tree.item.test.basics.js +0 -30
  101. package/package.json +1 -1
  102. package/dist/button.test.form.d.ts +0 -1
  103. package/dist/button.test.form.js +0 -50
  104. package/dist/input.test.translations.js +0 -38
  105. package/dist/tag.test.translations.d.ts +0 -1
  106. package/dist/tag.test.translations.js +0 -25
  107. package/dist/textarea.test.translations.d.ts +0 -1
  108. package/dist/textarea.test.translations.js +0 -34
  109. /package/dist/{input.test.translations.d.ts → library/localize.test.d.ts} +0 -0
@@ -1,81 +1,77 @@
1
- import{css,unsafeCSS}from"lit";import visuallyHidden from"./styles/visually-hidden.js";const fieldSizingContent=unsafeCSS("\n field-sizing: content;\n");export default css`
2
- glide-core-private-label::part(tooltips-and-label) {
3
- align-items: flex-start;
4
- margin-block-start: var(--glide-core-spacing-sm);
5
- }
6
-
7
- .textarea-container {
8
- display: flex;
9
- }
1
+ import{css,unsafeCSS}from"lit";import visuallyHidden from"./styles/visually-hidden.js";const fieldSizingContent=unsafeCSS("\n field-sizing: content;\n");export default[css`
2
+ ${visuallyHidden(".character-count .hidden")}
3
+ `,css`
4
+ glide-core-private-label::part(tooltips-and-label) {
5
+ align-items: flex-start;
6
+ margin-block-start: var(--glide-core-spacing-sm);
7
+ }
10
8
 
11
- .description {
12
- display: block;
13
- }
9
+ .textarea-container {
10
+ display: flex;
11
+ }
14
12
 
15
- textarea {
16
- appearance: none;
17
- background-color: var(--glide-core-surface-base-lighter);
18
- border: 1px solid var(--glide-core-border-base-light);
19
- border-radius: 0.5rem;
20
- color: var(--glide-core-text-body-1);
21
- cursor: inherit;
22
- display: block;
23
- flex-grow: 1;
24
- font-family: var(--glide-core-body-xs-font-family);
25
- font-size: var(--glide-core-body-sm-font-size);
26
- font-weight: var(--glide-core-body-xs-font-weight);
27
- max-block-size: 5lh;
28
- min-block-size: 3lh;
29
- padding: var(--glide-core-spacing-xs) var(--glide-core-spacing-sm);
30
- resize: vertical;
31
- transition: border-color 200ms ease-in-out;
13
+ .description {
14
+ display: block;
15
+ }
32
16
 
33
- ${fieldSizingContent};
17
+ textarea {
18
+ appearance: none;
19
+ background-color: var(--glide-core-surface-base-lighter);
20
+ border: 1px solid var(--glide-core-border-base);
21
+ border-radius: 0.5rem;
22
+ color: var(--glide-core-text-body-1);
23
+ cursor: inherit;
24
+ display: block;
25
+ flex-grow: 1;
26
+ font-family: var(--glide-core-body-xs-font-family);
27
+ font-size: var(--glide-core-body-sm-font-size);
28
+ font-weight: var(--glide-core-body-xs-font-weight);
29
+ max-block-size: 5lh;
30
+ min-block-size: 3lh;
31
+ padding: var(--glide-core-spacing-xs) var(--glide-core-spacing-sm);
32
+ resize: vertical;
33
+ transition: border-color 200ms ease-in-out;
34
34
 
35
- &:hover {
36
- border-color: var(--glide-core-border-base);
37
- }
35
+ ${fieldSizingContent};
38
36
 
39
- &:focus {
40
- outline: none;
41
- }
37
+ &:focus {
38
+ outline: none;
39
+ }
42
40
 
43
- &:focus-visible,
44
- &:focus-visible[readonly] {
45
- border-color: var(--glide-core-border-focus);
46
- }
41
+ &:focus-visible,
42
+ &:focus-visible[readonly],
43
+ &:hover {
44
+ border-color: var(--glide-core-border-focus);
45
+ }
47
46
 
48
- &.error {
49
- border-color: var(--glide-core-status-error);
50
- }
47
+ &.error {
48
+ border-color: var(--glide-core-status-error);
49
+ }
51
50
 
52
- &[readonly] {
53
- border-color: transparent;
54
- outline: none;
55
- transition: none;
56
- }
51
+ &[readonly] {
52
+ border-color: transparent;
53
+ outline: none;
54
+ resize: none;
55
+ transition: none;
56
+ }
57
57
 
58
- &[disabled] {
59
- background-color: var(--glide-core-surface-disabled);
60
- border: 0.0625rem solid var(--glide-core-border-base-light);
61
- color: var(--glide-core-text-tertiary-disabled);
58
+ &[disabled] {
59
+ background-color: var(--glide-core-surface-disabled);
60
+ border: 0.0625rem solid var(--glide-core-border-base-light);
61
+ color: var(--glide-core-text-tertiary-disabled);
62
+ }
62
63
  }
63
- }
64
-
65
- .meta {
66
- column-gap: var(--glide-core-spacing-xs);
67
- display: flex;
68
- font-size: var(--glide-core-body-xs-font-size);
69
- justify-content: space-between;
70
- }
71
64
 
72
- .character-count {
73
- &.error {
74
- font-weight: var(--glide-core-font-weight-bold);
65
+ .meta {
66
+ column-gap: var(--glide-core-spacing-xs);
67
+ display: flex;
68
+ font-size: var(--glide-core-body-xs-font-size);
69
+ justify-content: space-between;
75
70
  }
76
71
 
77
- .hidden {
78
- ${visuallyHidden};
72
+ .character-count {
73
+ &.error {
74
+ font-weight: var(--glide-core-font-weight-bold);
75
+ }
79
76
  }
80
- }
81
- `;
77
+ `];
@@ -7,171 +7,171 @@ it('registers', async () => {
7
7
  expect(window.customElements.get('glide-core-textarea')).to.equal(GlideCoreTextarea);
8
8
  });
9
9
  it('is accessible', async () => {
10
- const element = await fixture(html `<glide-core-textarea
10
+ const component = await fixture(html `<glide-core-textarea
11
11
  value="value"
12
12
  label="label"
13
13
  ></glide-core-textarea>`);
14
- await expect(element).to.be.accessible();
14
+ await expect(component).to.be.accessible();
15
15
  });
16
16
  it('renders a textarea with two rows and value when attribute `value` is set ', async () => {
17
- const element = await fixture(html `<glide-core-textarea
17
+ const component = await fixture(html `<glide-core-textarea
18
18
  value="value"
19
19
  label="label"
20
20
  ></glide-core-textarea>`);
21
- const textarea = element.shadowRoot.querySelector('textarea');
22
- expect(element);
23
- expect(element).to.have.attribute('rows', '2');
21
+ const textarea = component.shadowRoot.querySelector('textarea');
22
+ expect(component);
23
+ expect(component).to.have.attribute('rows', '2');
24
24
  expect(textarea).to.exist;
25
25
  expect(textarea).to.have.attribute('rows', '2');
26
26
  });
27
27
  it('renders the `rows` attribute on the textarea when set', async () => {
28
- const element = await fixture(html `<glide-core-textarea
28
+ const component = await fixture(html `<glide-core-textarea
29
29
  value="value"
30
30
  label="label"
31
31
  rows="5"
32
32
  ></glide-core-textarea>`);
33
- const textarea = element.shadowRoot.querySelector('textarea');
33
+ const textarea = component.shadowRoot.querySelector('textarea');
34
34
  expect(textarea).to.have.attribute('rows', '5');
35
35
  });
36
36
  it('renders a label when attribute `label` is set', async () => {
37
- const element = await fixture(html `<glide-core-textarea
37
+ const component = await fixture(html `<glide-core-textarea
38
38
  value="value"
39
39
  label="label"
40
40
  ></glide-core-textarea>`);
41
- const label = element.shadowRoot.querySelector('label');
41
+ const label = component.shadowRoot.querySelector('label');
42
42
  expect(label).to.exist;
43
43
  expect(label?.textContent?.trim()).to.be.equal('label');
44
44
  });
45
45
  it('renders the textarea as readonly when attribute `readonly` is set', async () => {
46
- const element = await fixture(html `<glide-core-textarea
46
+ const component = await fixture(html `<glide-core-textarea
47
47
  value="value"
48
48
  label="label"
49
49
  readonly
50
50
  ></glide-core-textarea>`);
51
- const textarea = element.shadowRoot.querySelector('textarea');
51
+ const textarea = component.shadowRoot.querySelector('textarea');
52
52
  expect(textarea).to.have.attribute('readonly');
53
53
  });
54
54
  it('renders the textarea as disabled when attribute `disabled` is set', async () => {
55
- const element = await fixture(html `<glide-core-textarea
55
+ const component = await fixture(html `<glide-core-textarea
56
56
  value="value"
57
57
  label="label"
58
58
  disabled
59
59
  ></glide-core-textarea>`);
60
- const textarea = element.shadowRoot.querySelector('textarea');
60
+ const textarea = component.shadowRoot.querySelector('textarea');
61
61
  expect(textarea).to.have.attribute('disabled');
62
62
  });
63
63
  it('renders the textarea with a placeholder when attribute `placeholder` is set', async () => {
64
- const element = await fixture(html `<glide-core-textarea
64
+ const component = await fixture(html `<glide-core-textarea
65
65
  value=""
66
66
  label="label"
67
67
  placeholder="placeholder"
68
68
  ></glide-core-textarea>`);
69
- const textarea = element.shadowRoot.querySelector('textarea');
69
+ const textarea = component.shadowRoot.querySelector('textarea');
70
70
  expect(textarea).to.have.attribute('placeholder', 'placeholder');
71
71
  });
72
72
  it('renders `required` on textarea when set', async () => {
73
- const element = await fixture(html `<glide-core-textarea
73
+ const component = await fixture(html `<glide-core-textarea
74
74
  value="value"
75
75
  label="label"
76
76
  required
77
77
  ></glide-core-textarea>`);
78
- const textarea = element.shadowRoot.querySelector('textarea');
78
+ const textarea = component.shadowRoot.querySelector('textarea');
79
79
  expect(textarea).to.have.attribute('required');
80
80
  });
81
81
  it('renders a `name` attribute on the textarea when set', async () => {
82
- const element = await fixture(html `<glide-core-textarea
82
+ const component = await fixture(html `<glide-core-textarea
83
83
  value="value"
84
84
  label="label"
85
85
  name="test-name"
86
86
  ></glide-core-textarea>`);
87
- const textarea = element.shadowRoot.querySelector('textarea');
87
+ const textarea = component.shadowRoot.querySelector('textarea');
88
88
  expect(textarea).to.have.attribute('name', 'test-name');
89
89
  });
90
90
  it('supports a "tooltip" slot', async () => {
91
- const element = await fixture(html `
91
+ const component = await fixture(html `
92
92
  <glide-core-textarea value="value" label="label" required>
93
93
  <div slot="tooltip">Tooltip</div>
94
94
  </glide-core-textarea>
95
95
  `);
96
- const assignedElements = element.shadowRoot
96
+ const assignedElements = component.shadowRoot
97
97
  ?.querySelector('slot[name="tooltip"]')
98
98
  ?.assignedElements();
99
99
  expect(assignedElements?.at(0)?.textContent).to.equal('Tooltip');
100
100
  });
101
101
  it('renders a slot with description', async () => {
102
- const element = await fixture(html `<glide-core-textarea value="value" label="label"
102
+ const component = await fixture(html `<glide-core-textarea value="value" label="label"
103
103
  ><span slot="description" data-test-content
104
104
  >Description</span
105
105
  ></glide-core-textarea
106
106
  >`);
107
- expect(element).to.exist;
108
- const contentRendered = element.querySelector('[data-test-content]');
107
+ expect(component).to.exist;
108
+ const contentRendered = component.querySelector('[data-test-content]');
109
109
  expect(contentRendered).to.exist;
110
110
  expect(contentRendered?.textContent).to.be.equal('Description');
111
111
  });
112
112
  it('displays visually hidden character count text for screenreaders', async () => {
113
- const element = await fixture(html `<glide-core-textarea
113
+ const component = await fixture(html `<glide-core-textarea
114
114
  label="label"
115
115
  maxlength="10"
116
116
  ></glide-core-textarea>`);
117
- const maxCharacterCountAnnouncement = element.shadowRoot?.querySelector('[data-test="character-count-announcement"]');
117
+ const maxCharacterCountAnnouncement = component.shadowRoot?.querySelector('[data-test="character-count-announcement"]');
118
118
  expect(maxCharacterCountAnnouncement?.textContent?.trim()).to.equal('Character count 0 of 10');
119
119
  });
120
120
  it('renders a character count when attribute `maxlength` is set greater than zero', async () => {
121
- const element = await fixture(html `<glide-core-textarea value="value" label="label" maxlength="10"
121
+ const component = await fixture(html `<glide-core-textarea value="value" label="label" maxlength="10"
122
122
  ><span slot="description">Description</span></glide-core-textarea
123
123
  >`);
124
- const container = element.shadowRoot.querySelector('[data-test="character-count-text"]');
124
+ const container = component.shadowRoot.querySelector('[data-test="character-count-text"]');
125
125
  expect(container?.textContent?.trim()).to.be.equal('5/10');
126
126
  });
127
127
  it('does not render a character count when attribute `maxlength` is set less than than zero', async () => {
128
- const element = await fixture(html `<glide-core-textarea value="value" label="label" maxlength="0"
128
+ const component = await fixture(html `<glide-core-textarea value="value" label="label" maxlength="0"
129
129
  ><span slot="description">Description</span></glide-core-textarea
130
130
  >`);
131
- const container = element.shadowRoot?.querySelector('[data-test="character-count-container"]');
131
+ const container = component.shadowRoot?.querySelector('[data-test="character-count-container"]');
132
132
  expect(container).to.be.null;
133
133
  });
134
134
  it('focuses the textarea when the label is clicked', async () => {
135
- const element = await fixture(html `<glide-core-textarea
135
+ const component = await fixture(html `<glide-core-textarea
136
136
  value="value"
137
137
  label="label"
138
138
  ></glide-core-textarea>`);
139
- const label = element.shadowRoot.querySelector('label');
139
+ const label = component.shadowRoot.querySelector('label');
140
140
  label?.click();
141
- expect(element).to.have.focus;
142
- await expect(element.shadowRoot?.activeElement?.tagName.toLocaleLowerCase()).to.be.equal('textarea');
141
+ expect(component).to.have.focus;
142
+ await expect(component.shadowRoot?.activeElement?.tagName.toLocaleLowerCase()).to.be.equal('textarea');
143
143
  });
144
144
  it('has tabbable textarea', async () => {
145
- const element = await fixture(html `<glide-core-textarea
145
+ const component = await fixture(html `<glide-core-textarea
146
146
  value="value"
147
147
  label="label"
148
148
  ></glide-core-textarea>`);
149
149
  await sendKeys({ press: 'Tab' });
150
- expect(element).to.have.focus;
151
- await expect(element.shadowRoot?.activeElement?.tagName.toLocaleLowerCase()).to.be.equal('textarea');
150
+ expect(component).to.have.focus;
151
+ await expect(component.shadowRoot?.activeElement?.tagName.toLocaleLowerCase()).to.be.equal('textarea');
152
152
  });
153
153
  it('renders text when typed into text area', async () => {
154
- const element = await fixture(html `<glide-core-textarea value="" label="label"></glide-core-textarea>`);
155
- const textarea = element.shadowRoot.querySelector('textarea');
156
- element.focus();
154
+ const component = await fixture(html `<glide-core-textarea value="" label="label"></glide-core-textarea>`);
155
+ const textarea = component.shadowRoot.querySelector('textarea');
156
+ component.focus();
157
157
  await sendKeys({ type: 'test text' });
158
158
  expect(textarea?.value).to.equal('test text');
159
159
  });
160
160
  it('returns the content of the textarea when getting the `value` property', async () => {
161
- const element = await fixture(html `<glide-core-textarea value="" label="label"></glide-core-textarea>`);
162
- element.focus();
161
+ const component = await fixture(html `<glide-core-textarea value="" label="label"></glide-core-textarea>`);
162
+ component.focus();
163
163
  await sendKeys({ type: 'test text' });
164
- expect(element.value).to.equal('test text');
164
+ expect(component.value).to.equal('test text');
165
165
  });
166
166
  it('focuses the textarea when `focus()` is called', async () => {
167
- const element = await fixture(html `<glide-core-textarea value="" label="label"></glide-core-textarea>`);
168
- element.focus();
169
- await expect(element.shadowRoot?.activeElement?.tagName.toLocaleLowerCase()).to.be.equal('textarea');
167
+ const component = await fixture(html `<glide-core-textarea value="" label="label"></glide-core-textarea>`);
168
+ component.focus();
169
+ await expect(component.shadowRoot?.activeElement?.tagName.toLocaleLowerCase()).to.be.equal('textarea');
170
170
  });
171
171
  it('blurs the textarea when `blur` is called', async () => {
172
- const element = await fixture(html `<glide-core-textarea value="" label="label"></glide-core-textarea>`);
173
- element.focus();
174
- await expect(element.shadowRoot?.activeElement?.tagName.toLocaleLowerCase()).to.be.equal('textarea');
175
- element.blur();
176
- await expect(element.shadowRoot?.activeElement?.tagName.toLocaleLowerCase()).to.not.equal('textarea');
172
+ const component = await fixture(html `<glide-core-textarea value="" label="label"></glide-core-textarea>`);
173
+ component.focus();
174
+ await expect(component.shadowRoot?.activeElement?.tagName.toLocaleLowerCase()).to.be.equal('textarea');
175
+ component.blur();
176
+ await expect(component.shadowRoot?.activeElement?.tagName.toLocaleLowerCase()).to.not.equal('textarea');
177
177
  });
package/dist/toasts.d.ts CHANGED
@@ -19,6 +19,11 @@ export default class GlideCoreToasts extends LitElement {
19
19
  #private;
20
20
  static shadowRootOptions: ShadowRootInit;
21
21
  static styles: import("lit").CSSResult[];
22
+ /**
23
+ * @param {number} [toast.duration=5000]
24
+ * Optional: Number of milliseconds before the Toast auto-hides.
25
+ * Minimum: `5000`. Default: `5000`. For a Toast that never auto-hides, set to `Infinity`
26
+ * */
22
27
  add(toast: Toast): import("./toasts.toast.js").default & {
23
28
  variant: "success" | "informational";
24
29
  label: string;
@@ -3,7 +3,7 @@ import{css}from"lit";export default[css`
3
3
  background-color: transparent;
4
4
  border: none;
5
5
  display: none;
6
- flex-direction: column;
6
+ flex-direction: column-reverse;
7
7
  gap: var(--glide-core-spacing-md);
8
8
  inline-size: 24.25rem;
9
9
  inset-block-start: 0;
@@ -1,4 +1,6 @@
1
1
  import{css}from"lit";import focusOutline from"./styles/focus-outline.js";export default[css`
2
+ ${focusOutline(".toggle-and-input:has(input:focus-visible)")}
3
+ `,css`
2
4
  /*
3
5
  Most states are handled on the host. But ":checked" is handled on the input
4
6
  because browsers don't support that class on the host. And using attribute
@@ -30,7 +32,6 @@ Use the ":checked" pseudo class on the host and throughout when browsers support
30
32
  position: relative;
31
33
 
32
34
  &:has(input:focus-visible) {
33
- ${focusOutline};
34
35
  outline-offset: 4px;
35
36
  }
36
37
 
@@ -68,3 +68,14 @@ it('is unchecked after being clicked then forcibly unchecked via an "input" list
68
68
  const input = component.shadowRoot?.querySelector('[data-test="input"]');
69
69
  expect(input?.getAttribute('aria-checked')).to.equal('false');
70
70
  });
71
+ it('remains unchecked when its "click" event is canceled', async () => {
72
+ const component = await fixture(html `<glide-core-toggle label="Label"></glide-core-toggle>`);
73
+ component.addEventListener('click', async (event) => {
74
+ event.preventDefault();
75
+ });
76
+ component.click();
77
+ expect(component.hasAttribute('checked')).to.be.false;
78
+ expect(component.checked).to.equal(false);
79
+ const input = component.shadowRoot?.querySelector('[data-test="input"]');
80
+ expect(input?.getAttribute('aria-checked')).to.equal('false');
81
+ });
package/dist/tooltip.js CHANGED
@@ -1 +1 @@
1
- var __decorate=this&&this.__decorate||function(e,t,o,l){var i,s=arguments.length,a=s<3?t:null===l?l=Object.getOwnPropertyDescriptor(t,o):l;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,o,l);else for(var n=e.length-1;n>=0;n--)(i=e[n])&&(a=(s<3?i(a):s>3?i(t,o,a):i(t,o))||a);return s>3&&a&&Object.defineProperty(t,o,a),a};import{LitElement,html}from"lit";import{arrow,autoUpdate,computePosition,flip,limitShift,offset,shift}from"@floating-ui/dom";import{choose}from"lit/directives/choose.js";import{classMap}from"lit/directives/class-map.js";import{createRef,ref}from"lit/directives/ref.js";import{customElement,property,state}from"lit/decorators.js";import{ifDefined}from"lit/directives/if-defined.js";import ow,{owSlot}from"./library/ow.js";import styles from"./tooltip.styles.js";let GlideCoreTooltip=class GlideCoreTooltip extends LitElement{constructor(){super(...arguments),this.offset=4,this.effectivePlacement=this.placement??"bottom",this.#e=createRef(),this.#t=createRef(),this.#o=!1,this.#l=!1,this.#i=createRef(),this.#s=createRef(),this.#a=createRef()}static{this.shadowRootOptions={...LitElement.shadowRootOptions,delegatesFocus:!0,mode:"closed"}}static{this.styles=styles}get disabled(){return this.#o}set disabled(e){this.#o=e,this.open&&!e?this.#n():this.#r()}get open(){return this.#l}set open(e){this.#l=e,e&&!this.disabled?this.#n():this.#r()}disconnectedCallback(){super.disconnectedCallback(),clearTimeout(this.#f),clearTimeout(this.#p)}firstUpdated(){owSlot(this.#t.value),owSlot(this.#s.value),ow(this.#a.value,ow.object.instanceOf(HTMLElement)),this.#a.value.popover="manual",this.open&&!this.disabled&&this.#n()}render(){return html`<div class="component" @mouseover="${this.#h}" @mouseout="${this.#d}"><div aria-labelledby="${ifDefined(this.disabled?void 0:"tooltip")}" class="target" data-test="target" slot="target" @focusin="${this.#c}" @focusout="${this.#m}" @keydown="${this.#u}" ${ref(this.#i)}><slot @slotchange="${this.#v}" ${ref(this.#s)} name="target"></slot></div><div class="${classMap({tooltip:!0,[this.effectivePlacement]:!0})}" id="tooltip" data-test="tooltip" data-open-delay="300" data-close-delay="200" role="${ifDefined(this.disabled?void 0:"tooltip")}" ${ref(this.#a)}><div class="${classMap({arrow:!0,[this.effectivePlacement]:!0})}" data-test="arrow" ${ref(this.#e)}>${choose(this.effectivePlacement,[["top",()=>html`<svg viewBox="0 0 10 6" fill="none"><path d="M4.23178 5.07814C4.63157 5.55789 5.36843 5.55789 5.76822 5.07813L10 -7.9486e-08L-2.62268e-07 3.57628e-07L4.23178 5.07814Z" fill="#212121"/></svg>`],["right",()=>html`<svg viewBox="0 0 6 10" fill="none"><path d="M0.921865 4.23178C0.442111 4.63157 0.442112 5.36843 0.921866 5.76822L6 10L6 -2.62268e-07L0.921865 4.23178Z" fill="#212121"/></svg>`],["bottom",()=>html`<svg viewBox="0 0 10 6" fill="none"><path d="M4.23178 0.921865C4.63157 0.442111 5.36843 0.442112 5.76822 0.921866L10 6L-2.62268e-07 6L4.23178 0.921865Z" fill="#212121"/></svg>`],["left",()=>html`<svg viewBox="0 0 6 10" fill="none"><path d="M5.07814 4.23178C5.55789 4.63157 5.55789 5.36843 5.07813 5.76822L-4.37114e-07 10L0 -2.62268e-07L5.07814 4.23178Z" fill="#212121"/></svg>`]])}</div><span aria-label="${ifDefined(this.disabled?void 0:"Tooltip: ")}"></span><slot class="default-slot" @slotchange="${this.#R}" ${ref(this.#t)}></slot></div></div>`}#e;#g;#f;#t;#o;#l;#p;#i;#s;#a;#E(){clearTimeout(this.#f)}#r(){this.#a.value?.hidePopover(),this.#g&&this.#g()}#R(){owSlot(this.#t.value)}#c(){this.open=!0}#m(){this.open=!1}#u(e){"Escape"===e.key&&(this.open=!1)}#d(){this.#w(),clearTimeout(this.#p)}#h(){ow(this.#a.value,ow.object.instanceOf(HTMLElement)),this.#E(),this.#p=setTimeout((()=>{this.open=!0}),Number(this.#a.value.dataset.openDelay))}#v(){owSlot(this.#s.value)}#w(){ow(this.#a.value,ow.object.instanceOf(HTMLElement)),this.#f=setTimeout((()=>{this.open=!1}),Number(this.#a.value.dataset.closeDelay))}#n(){this.disabled||(this.#g?.(),this.#i.value&&this.#a.value&&(this.#g=autoUpdate(this.#i.value,this.#a.value,(()=>{(async()=>{if(this.#i.value&&this.#a.value&&this.#e.value){const{x:e,y:t,placement:o,middlewareData:l}=await computePosition(this.#i.value,this.#a.value,{placement:this.placement,middleware:[offset(this.offset),flip({fallbackStrategy:"initialPlacement"}),shift({limiter:limitShift({offset:20})}),arrow({element:this.#e.value})]});Object.assign(this.#a.value.style,{left:`${e}px`,top:`${t}px`}),Object.assign(this.#e.value.style,{left:l.arrow?.x?`${l.arrow.x}px`:null,top:l.arrow?.y?`${l.arrow.y}px`:null}),this.effectivePlacement=o,this.#a.value.showPopover()}})()}))))}};__decorate([property({reflect:!0,type:Boolean})],GlideCoreTooltip.prototype,"disabled",null),__decorate([property({reflect:!0,type:Number})],GlideCoreTooltip.prototype,"offset",void 0),__decorate([property({reflect:!0,type:Boolean})],GlideCoreTooltip.prototype,"open",null),__decorate([property()],GlideCoreTooltip.prototype,"placement",void 0),__decorate([state()],GlideCoreTooltip.prototype,"effectivePlacement",void 0),GlideCoreTooltip=__decorate([customElement("glide-core-tooltip")],GlideCoreTooltip);export default GlideCoreTooltip;
1
+ var __decorate=this&&this.__decorate||function(e,t,o,l){var i,s=arguments.length,a=s<3?t:null===l?l=Object.getOwnPropertyDescriptor(t,o):l;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,o,l);else for(var n=e.length-1;n>=0;n--)(i=e[n])&&(a=(s<3?i(a):s>3?i(t,o,a):i(t,o))||a);return s>3&&a&&Object.defineProperty(t,o,a),a};import{LitElement,html}from"lit";import{arrow,autoUpdate,computePosition,flip,limitShift,offset,shift}from"@floating-ui/dom";import{choose}from"lit/directives/choose.js";import{classMap}from"lit/directives/class-map.js";import{createRef,ref}from"lit/directives/ref.js";import{customElement,property,state}from"lit/decorators.js";import{ifDefined}from"lit/directives/if-defined.js";import ow,{owSlot}from"./library/ow.js";import styles from"./tooltip.styles.js";let GlideCoreTooltip=class GlideCoreTooltip extends LitElement{constructor(){super(...arguments),this.offset=4,this.effectivePlacement=this.placement??"bottom",this.#e=createRef(),this.#t=createRef(),this.#o=!1,this.#l=!1,this.#i=createRef(),this.#s=createRef(),this.#a=createRef()}static{this.shadowRootOptions={...LitElement.shadowRootOptions,delegatesFocus:!0,mode:"closed"}}static{this.styles=styles}get disabled(){return this.#o}set disabled(e){this.#o=e,this.open&&!e?this.#n():this.#r()}get open(){return this.#l}set open(e){this.#l=e,e&&!this.disabled?this.#n():this.#r()}disconnectedCallback(){super.disconnectedCallback(),clearTimeout(this.#f),clearTimeout(this.#p)}firstUpdated(){owSlot(this.#t.value),owSlot(this.#s.value),ow(this.#a.value,ow.object.instanceOf(HTMLElement)),this.#a.value.popover="manual",this.open&&!this.disabled&&this.#n()}render(){return html`<div class="component" data-test="component" @mouseover="${this.#d}" @mouseout="${this.#h}"><div aria-labelledby="${ifDefined(this.disabled?void 0:"tooltip")}" class="target" data-test="target" slot="target" @focusin="${this.#c}" @focusout="${this.#m}" @keydown="${this.#u}" ${ref(this.#i)}><slot @slotchange="${this.#v}" ${ref(this.#s)} name="target"></slot></div><div class="${classMap({tooltip:!0,[this.effectivePlacement]:!0})}" id="tooltip" data-test="tooltip" data-open-delay="300" data-close-delay="200" role="${ifDefined(this.disabled?void 0:"tooltip")}" ${ref(this.#a)}><div class="${classMap({arrow:!0,[this.effectivePlacement]:!0})}" data-test="arrow" ${ref(this.#e)}>${choose(this.effectivePlacement,[["top",()=>html`<svg viewBox="0 0 10 6" fill="none"><path d="M4.23178 5.07814C4.63157 5.55789 5.36843 5.55789 5.76822 5.07813L10 -7.9486e-08L-2.62268e-07 3.57628e-07L4.23178 5.07814Z" fill="#212121"/></svg>`],["right",()=>html`<svg viewBox="0 0 6 10" fill="none"><path d="M0.921865 4.23178C0.442111 4.63157 0.442112 5.36843 0.921866 5.76822L6 10L6 -2.62268e-07L0.921865 4.23178Z" fill="#212121"/></svg>`],["bottom",()=>html`<svg viewBox="0 0 10 6" fill="none"><path d="M4.23178 0.921865C4.63157 0.442111 5.36843 0.442112 5.76822 0.921866L10 6L-2.62268e-07 6L4.23178 0.921865Z" fill="#212121"/></svg>`],["left",()=>html`<svg viewBox="0 0 6 10" fill="none"><path d="M5.07814 4.23178C5.55789 4.63157 5.55789 5.36843 5.07813 5.76822L-4.37114e-07 10L0 -2.62268e-07L5.07814 4.23178Z" fill="#212121"/></svg>`]])}</div><span aria-label="${ifDefined(this.disabled?void 0:"Tooltip: ")}"></span><slot class="default-slot" @slotchange="${this.#R}" ${ref(this.#t)}></slot></div></div>`}#e;#g;#f;#t;#o;#l;#p;#i;#s;#a;#E(){clearTimeout(this.#f)}#r(){this.#a.value?.hidePopover(),this.#g&&this.#g()}#R(){owSlot(this.#t.value)}#c(){this.open=!0}#m(){this.open=!1}#u(e){"Escape"===e.key&&(this.open=!1)}#h(){this.#w(),clearTimeout(this.#p)}#d(){ow(this.#a.value,ow.object.instanceOf(HTMLElement)),this.#E(),this.#p=setTimeout((()=>{this.open=!0}),Number(this.#a.value.dataset.openDelay))}#v(){owSlot(this.#s.value)}#w(){ow(this.#a.value,ow.object.instanceOf(HTMLElement)),this.#f=setTimeout((()=>{this.open=!1}),Number(this.#a.value.dataset.closeDelay))}#n(){this.disabled||(this.#g?.(),this.#i.value&&this.#a.value&&(this.#g=autoUpdate(this.#i.value,this.#a.value,(()=>{(async()=>{if(this.#i.value&&this.#a.value&&this.#e.value){const{x:e,y:t,placement:o,middlewareData:l}=await computePosition(this.#i.value,this.#a.value,{placement:this.placement,middleware:[offset(this.offset),flip({fallbackStrategy:"initialPlacement"}),shift({limiter:limitShift({offset:20})}),arrow({element:this.#e.value})]});Object.assign(this.#a.value.style,{left:`${e}px`,top:`${t}px`}),Object.assign(this.#e.value.style,{left:l.arrow?.x?`${l.arrow.x}px`:null,top:l.arrow?.y?`${l.arrow.y}px`:null}),this.effectivePlacement=o,this.#a.value.showPopover()}})()}))))}};__decorate([property({reflect:!0,type:Boolean})],GlideCoreTooltip.prototype,"disabled",null),__decorate([property({reflect:!0,type:Number})],GlideCoreTooltip.prototype,"offset",void 0),__decorate([property({reflect:!0,type:Boolean})],GlideCoreTooltip.prototype,"open",null),__decorate([property()],GlideCoreTooltip.prototype,"placement",void 0),__decorate([state()],GlideCoreTooltip.prototype,"effectivePlacement",void 0),GlideCoreTooltip=__decorate([customElement("glide-core-tooltip")],GlideCoreTooltip);export default GlideCoreTooltip;
@@ -1,5 +1,7 @@
1
1
  import{css}from"lit";import focusOutline from"./styles/focus-outline.js";export default[css`
2
- @keyframes tooltip {
2
+ ${focusOutline(".target:focus-visible")}
3
+ `,css`
4
+ @keyframes opacity-and-scale {
3
5
  from {
4
6
  opacity: 0;
5
7
  transform: scale(0.95);
@@ -11,6 +13,12 @@ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";export
11
13
  }
12
14
  }
13
15
 
16
+ @media (prefers-reduced-motion: reduce) {
17
+ .tooltip:popover-open {
18
+ animation: none !important;
19
+ }
20
+ }
21
+
14
22
  :host {
15
23
  display: inline-block;
16
24
  }
@@ -28,9 +36,9 @@ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";export
28
36
  background-color: transparent;
29
37
  border-width: 0;
30
38
 
31
- /*
32
- Additional whitespace from line height and the tooltip won't be vertically
33
- centered against its target.
39
+ /*
40
+ Additional whitespace from line height and the tooltip won't be vertically
41
+ centered against its target.
34
42
  */
35
43
  display: flex;
36
44
 
@@ -43,10 +51,6 @@ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";export
43
51
  outline: none;
44
52
  }
45
53
 
46
- &:focus-visible {
47
- ${focusOutline};
48
- }
49
-
50
54
  ::slotted svg {
51
55
  display: block;
52
56
  }
@@ -68,19 +72,19 @@ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";export
68
72
  }
69
73
 
70
74
  &:popover-open {
71
- animation: tooltip 250ms cubic-bezier(0.25, 0, 0.3, 1);
75
+ animation: opacity-and-scale 250ms cubic-bezier(0.25, 0, 0.3, 1);
72
76
  display: flex;
73
77
 
74
- /*
78
+ /*
75
79
  Elements with a "popover" attribute don't allow overflow. So the arrow can't
76
- be positioned with "position: absolute". Relative positioning is used instead.
77
- Flex is used to get the arrow on the correct side of the tooltip. Floating UI
78
- handles the rest.
79
-
80
- A simple "transform" could replace Floating UI for the arrow if not for a Chrome
81
- bug with "popover" when "scale()" is animated on the popover or a container within
82
- it. With "transform" on the arrow, the arrow is initially offset by a couple pixels
83
- before landing in the correct position when the animation finishes. It only happens
80
+ be positioned with "position: absolute". Relative positioning is used instead.
81
+ Flex is used to get the arrow on the correct side of the tooltip. Floating UI
82
+ handles the rest.
83
+
84
+ A simple "transform" could replace Floating UI for the arrow if not for a Chrome
85
+ bug with "popover" when "scale()" is animated on the popover or a container within
86
+ it. With "transform" on the arrow, the arrow is initially offset by a couple pixels
87
+ before landing in the correct position when the animation finishes. It only happens
84
88
  when the tooltip is left or right of its target.
85
89
  */
86
90
 
@@ -117,7 +117,7 @@ it('is visible on "mouseover"', async () => {
117
117
  assert(tooltip);
118
118
  tooltip.dataset.openDelay = '0';
119
119
  component.shadowRoot
120
- ?.querySelector('.component')
120
+ ?.querySelector('[data-test="component"')
121
121
  ?.dispatchEvent(new MouseEvent('mouseover'));
122
122
  // Wait for Floating UI and the open delay.
123
123
  await aTimeout(0);
@@ -132,7 +132,7 @@ it('is hidden on "mouseover" when disabled', async () => {
132
132
  assert(tooltip);
133
133
  tooltip.dataset.openDelay = '0';
134
134
  component.shadowRoot
135
- ?.querySelector('.component')
135
+ ?.querySelector('[data-test="component"')
136
136
  ?.dispatchEvent(new MouseEvent('mouseover'));
137
137
  // Wait for Floating UI.
138
138
  await aTimeout(0);
@@ -147,13 +147,13 @@ it('is hidden on "mouseout"', async () => {
147
147
  assert(tooltip);
148
148
  tooltip.dataset.openDelay = '0';
149
149
  component.shadowRoot
150
- ?.querySelector('.component')
150
+ ?.querySelector('[data-test="component"')
151
151
  ?.dispatchEvent(new MouseEvent('mouseover'));
152
152
  // Wait for Floating UI and the open delay.
153
153
  await aTimeout(0);
154
154
  tooltip.dataset.closeDelay = '0';
155
155
  component.shadowRoot
156
- ?.querySelector('.component')
156
+ ?.querySelector('[data-test="component"')
157
157
  ?.dispatchEvent(new MouseEvent('mouseout'));
158
158
  // Wait for the close delay.
159
159
  await aTimeout(0);
@@ -169,11 +169,11 @@ it('remains hidden if "mouseout" fires before the "mouseover" delay', async () =
169
169
  tooltip.dataset.openDelay = '1';
170
170
  tooltip.dataset.closeDelay = '0';
171
171
  component.shadowRoot
172
- ?.querySelector('.component')
172
+ ?.querySelector('[data-test="component"')
173
173
  ?.dispatchEvent(new MouseEvent('mouseover'));
174
174
  expect(tooltip?.checkVisibility()).to.be.false;
175
175
  component.shadowRoot
176
- ?.querySelector('.component')
176
+ ?.querySelector('[data-test="component"')
177
177
  ?.dispatchEvent(new MouseEvent('mouseout'));
178
178
  await aTimeout(1);
179
179
  expect(tooltip.checkVisibility()).to.be.false;
@@ -1 +1 @@
1
- const translation={$code:"en",$name:"English",$dir:"ltr",close:"Close",dismiss:"Dismiss",open:"Open",selectAll:"Select all",moreInformation:"More information",nextTab:"Next tab",notifications:"Notifications",previousTab:"Previous tab",announcedCharacterCount:(e,o)=>`Character count ${e} of ${o}`,displayedCharacterCount:(e,o)=>`${e}/${o}`,clearEntry:e=>`Clear ${e} entry`,removeTag:e=>`Remove tag: ${e}`,actionsFor:e=>`Actions for ${e}`};export default translation;
1
+ const translation={$code:"en",$name:"English",$dir:"ltr",close:"Close",dismiss:"Dismiss",open:"Open",selectAll:"Select all",moreInformation:"More information",notifications:"Notifications",nextTab:"Next tab",previousTab:"Previous tab",announcedCharacterCount:(e,o)=>`Character count ${e} of ${o}`,displayedCharacterCount:(e,o)=>`${e}/${o}`,clearEntry:e=>`Clear ${e} entry`,removeTag:e=>`Remove tag: ${e}`,actionsFor:e=>`Actions for ${e}`};export default translation;
@@ -1,3 +1,5 @@
1
1
  import type { Translation } from '../library/localize.js';
2
- declare const translation: Translation;
2
+ export declare const PENDING_STRINGS: readonly [];
3
+ type PendingTranslation = (typeof PENDING_STRINGS)[number];
4
+ declare const translation: Omit<Translation, PendingTranslation>;
3
5
  export default translation;
@@ -1 +1 @@
1
- const translation={$code:"fr",$name:"French",$dir:"ltr",close:"Fermer",dismiss:"Congédier",open:"Ouvrir",selectAll:"Tout sélectionner",moreInformation:"Plus d’informations",notifications:"Notifications",nextTab:"Next tab",previousTab:"Previous tab",announcedCharacterCount:(r,e)=>`Character count ${r} of ${e}`,displayedCharacterCount:(r,e)=>`${r}/${e}`,clearEntry:r=>`Clear ${r} entry`,removeTag:r=>`Supprimer la balise : ${r}`,actionsFor:r=>`Actions for ${r}`};export default translation;
1
+ export const PENDING_STRINGS=[];const translation={$code:"fr",$name:"French",$dir:"ltr",close:"Fermer",dismiss:"Congédier",open:"Ouvrir",selectAll:"Tout sélectionner",moreInformation:"Plus d’informations",notifications:"Notifications",nextTab:"Onglet suivant",previousTab:"Onglet précédent",announcedCharacterCount:(e,r)=>`Nombre de caractères ${e} de ${r}`,displayedCharacterCount:(e,r)=>`${e}/${r}`,clearEntry:e=>`Effacer l'entrée ${e}`,removeTag:e=>`Supprimer la balise : ${e}`,actionsFor:e=>`Actions pour ${e}`};export default translation;
@@ -1,3 +1,5 @@
1
1
  import type { Translation } from '../library/localize.js';
2
- declare const translation: Translation;
2
+ export declare const PENDING_STRINGS: readonly [];
3
+ type PendingTranslation = (typeof PENDING_STRINGS)[number];
4
+ declare const translation: Omit<Translation, PendingTranslation>;
3
5
  export default translation;
@@ -1 +1 @@
1
- const translation={$code:"ja",$name:"Japanese",$dir:"ltr",close:"閉じる",dismiss:"無視",open:"オープン",selectAll:"すべて選択",moreInformation:"詳細情報",notifications:"通知",nextTab:"Next tab",previousTab:"Previous tab",announcedCharacterCount:(a,t)=>`Character count ${a} of ${t}`,displayedCharacterCount:(a,t)=>`${a}/${t}`,clearEntry:a=>`Clear ${a} entry`,removeTag:a=>`タグを削除: ${a}`,actionsFor:a=>`Actions for ${a}`};export default translation;
1
+ export const PENDING_STRINGS=[];const translation={$code:"ja",$name:"Japanese",$dir:"ltr",close:"閉じる",dismiss:"無視",open:"オープン",selectAll:"すべて選択",moreInformation:"詳細情報",notifications:"通知",nextTab:"Onglet suivant",previousTab:"Onglet précédent",announcedCharacterCount:(n,t)=>`${t} 文字数の${n}`,displayedCharacterCount:(n,t)=>`${n}/${t}`,clearEntry:n=>`${n}エントリのクリア`,removeTag:n=>`タグを削除: ${n}`,actionsFor:n=>`${n}のアクション`};export default translation;