vueless 1.4.12-beta.15 → 1.4.12-beta.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "1.4.12-beta.15",
3
+ "version": "1.4.12-beta.17",
4
4
  "description": "Vue Styleless UI Component Library, powered by Tailwind CSS.",
5
5
  "author": "Johnny Grid <hello@vueless.com> (https://vueless.com)",
6
6
  "homepage": "https://vueless.com",
@@ -2,7 +2,7 @@ export default /*tw*/ {
2
2
  button: {
3
3
  base: `
4
4
  flex items-center justify-center font-medium leading-snug! whitespace-nowrap
5
- border border-solid outline-solid outline-transparent transition cursor-pointer
5
+ border border-solid outline-solid outline-medium outline-transparent transition cursor-pointer
6
6
  focus-visible:outline-medium focus-visible:outline-offset-2 focus-visible:outline-{color}
7
7
  disabled:cursor-not-allowed disabled:outline-0 disabled:outline-offset-0
8
8
  `,
@@ -5,7 +5,7 @@ export default /*tw*/ {
5
5
  text-{color} decoration-{color} underline-offset-4
6
6
  hover:text-{color}-lifted hover:decoration-{color}-lifted
7
7
  active:text-{color}-accented active:decoration-{color}-accented
8
- outline-solid outline-transparent
8
+ outline-solid outline-medium outline-transparent
9
9
  focus-visible:outline-medium focus-visible:rounded-small
10
10
  focus-visible:outline-offset-4 focus-visible:outline-{color}
11
11
  `,
@@ -1,7 +1,7 @@
1
1
  export default /*tw*/ {
2
2
  wrapper: {
3
3
  base: `
4
- relative inline-block h-max rounded-medium transition cursor-pointer outline-solid outline-transparent
4
+ relative inline-block h-max rounded-medium transition cursor-pointer outline-solid outline-medium outline-transparent
5
5
  focus-visible:outline-medium focus-visible:outline-offset-2 focus-visible:outline-primary`,
6
6
  variants: {
7
7
  disabled: {
@@ -3,7 +3,7 @@ export default /*tw*/ {
3
3
  base: `
4
4
  {UDropdown}
5
5
  inline-flex gap-0.5 items-center justify-between rounded-small h-max
6
- outline-solid outline-transparent
6
+ outline-solid outline-0 outline-transparent
7
7
  focus-visible:outline-offset-4
8
8
  `,
9
9
  defaults: {
@@ -56,7 +56,7 @@ export default /*tw*/ {
56
56
  timepicker: "mt-2 pl-1 pt-3 text-medium flex items-stretch justify-between gap-2 border-t border-muted",
57
57
  timepickerLabel: "w-full self-center",
58
58
  timepickerInputWrapper: `
59
- flex items-center rounded-medium border border-solid border-default outline-solid outline-transparent
59
+ flex items-center rounded-medium border border-solid border-default outline-solid outline-small outline-transparent
60
60
  hover:focus-within:border-primary focus-within:border-primary
61
61
  focus-within:outline focus-within:outline-small focus-within:outline-primary
62
62
  `,
@@ -12,7 +12,7 @@ export default /*tw*/ {
12
12
  },
13
13
  checkbox: {
14
14
  base: `
15
- {>checkboxBase} ml-0 bg-default border border-solid border-default outline-solid outline-transparent
15
+ {>checkboxBase} ml-0 bg-default border border-solid border-default outline-solid outline-medium outline-transparent
16
16
  appearance-none p-0 print:color-adjust-exact inline-block align-middle bg-origin-border select-none shrink-0
17
17
  hover:border-lifted
18
18
  active:border-{color} active:bg-{color}/15
@@ -119,6 +119,16 @@ function deactivate() {
119
119
  customView.value = View.Day;
120
120
  }
121
121
 
122
+ function deactivateOnBlur(event: FocusEvent) {
123
+ const related = event.relatedTarget as HTMLElement | null;
124
+
125
+ if (related && wrapperRef.value?.contains(related)) {
126
+ return;
127
+ }
128
+
129
+ deactivate();
130
+ }
131
+
122
132
  function onUserFormatDateChange(value: string) {
123
133
  userFormatDate.value = value.trim();
124
134
  }
@@ -279,7 +289,7 @@ watchEffect(() => {
279
289
  </script>
280
290
 
281
291
  <template>
282
- <div v-bind="wrapperAttrs" ref="wrapper" :data-test="getDataTest()">
292
+ <div v-bind="wrapperAttrs" ref="wrapper" :data-test="getDataTest()" @focusout="deactivateOnBlur">
283
293
  <UInput
284
294
  :id="elementId"
285
295
  :key="String(userFormatDate)"
@@ -356,6 +356,53 @@ describe("UDatePicker.vue", () => {
356
356
  expect(calendar.isVisible()).toBe(true);
357
357
  });
358
358
 
359
+ it("Calendar – hides when focus leaves the whole component", async () => {
360
+ const component = mount(UDatePicker, {
361
+ props: {
362
+ modelValue: new Date(),
363
+ },
364
+ attachTo: document.body,
365
+ });
366
+
367
+ const input = component.getComponent(UInput).find("input");
368
+ const calendar = component.findComponent({ name: "UCalendar" });
369
+
370
+ await input.trigger("focus");
371
+ expect(calendar.isVisible()).toBe(true);
372
+
373
+ const outsideElement = document.createElement("button");
374
+
375
+ document.body.appendChild(outsideElement);
376
+
377
+ await component.trigger("focusout", { relatedTarget: outsideElement });
378
+
379
+ expect(calendar.isVisible()).toBe(false);
380
+
381
+ outsideElement.remove();
382
+ component.unmount();
383
+ });
384
+
385
+ it("Calendar – stays open when focus moves inside the component", async () => {
386
+ const component = mount(UDatePicker, {
387
+ props: {
388
+ modelValue: new Date(),
389
+ },
390
+ attachTo: document.body,
391
+ });
392
+
393
+ const input = component.getComponent(UInput).find("input");
394
+ const calendar = component.findComponent({ name: "UCalendar" });
395
+
396
+ await input.trigger("focus");
397
+ expect(calendar.isVisible()).toBe(true);
398
+
399
+ await component.trigger("focusout", { relatedTarget: calendar.element });
400
+
401
+ expect(calendar.isVisible()).toBe(true);
402
+
403
+ component.unmount();
404
+ });
405
+
359
406
  it("Calendar – hides when escape key is pressed on calendar", async () => {
360
407
  const component = mount(UDatePicker, {
361
408
  props: {
@@ -12,7 +12,7 @@ export default /*tw*/ {
12
12
  wrapper: {
13
13
  base: `
14
14
  flex gap-3 w-full px-3 relative bg-default transition
15
- border border-solid rounded-medium border-default outline-solid outline-transparent
15
+ border border-solid rounded-medium border-default outline-solid outline-small outline-transparent
16
16
  hover:border-lifted hover:focus-within:border-primary focus-within:border-primary
17
17
  focus-within:outline focus-within:outline-small focus-within:outline-primary focus-within:transition
18
18
  `,
@@ -3,7 +3,7 @@ export default /*tw*/ {
3
3
  dropzone: {
4
4
  base: `
5
5
  p-3 size-auto w-full bg-default transition
6
- rounded-medium border border-solid border-default outline-solid outline-transparent
6
+ rounded-medium border border-solid border-default outline-solid outline-small outline-transparent
7
7
  hover:border-lifted hover:focus-within:border-primary focus-within:border-primary
8
8
  focus-within:outline focus-within:outline-small focus-within:outline-primary
9
9
  `,
@@ -580,6 +580,7 @@ const {
580
580
  <UButton
581
581
  round
582
582
  square
583
+ tabindex="-1"
583
584
  v-bind="addOptionButtonAttrs"
584
585
  :data-test="getDataTest('add-button')"
585
586
  @click="onClickAddOption"
@@ -3,7 +3,7 @@ export default /*tw*/ {
3
3
  radio: {
4
4
  base: `
5
5
  bg-default cursor-pointer transition
6
- border border-solid border-default rounded-full outline-solid outline-transparent
6
+ border border-solid border-default rounded-full outline-solid outline-medium outline-transparent
7
7
  appearance-none p-0 print:color-adjust-exact inline-block align-middle bg-origin-border select-none shrink-0
8
8
  hover:border-lifted hover:checked:border-{color}
9
9
  active:border-{color} active:bg-{color}/15
@@ -556,7 +556,7 @@ const {
556
556
 
557
557
  <div
558
558
  ref="wrapper"
559
- :tabindex="searchable || disabled ? -1 : 0"
559
+ :tabindex="disabled ? -1 : searchable && isOpen ? -1 : 0"
560
560
  role="combobox"
561
561
  :aria-expanded="ariaExpanded"
562
562
  aria-haspopup="listbox"
@@ -11,10 +11,10 @@ export default /*tw*/ {
11
11
  },
12
12
  wrapper: {
13
13
  base: `
14
- flex flex-row-reverse justify-between w-full min-h-full box-border relative
15
- rounded-medium border border-solid border-default bg-default outline-solid outline-transparent
16
- hover:border-lifted hover:transition hover:focus-within:border-primary focus-within:border-primary
17
- focus-within:outline focus-within:outline-small focus-within:outline-primary focus-within:transition
14
+ flex flex-row-reverse justify-between w-full min-h-full box-border relative transition
15
+ rounded-medium border border-solid border-default bg-default outline-solid outline-small outline-transparent
16
+ hover:border-lifted hover:focus-within:border-primary focus-within:border-primary
17
+ focus-within:outline focus-within:outline-small focus-within:outline-primary
18
18
  `,
19
19
  variants: {
20
20
  error: {
@@ -255,6 +255,63 @@ describe("USelect.vue", () => {
255
255
  expect(component.getComponent(UListbox).props("searchable")).toBe(true);
256
256
  });
257
257
 
258
+ it("Searchable – closes when the search input blurs to an outside element", async () => {
259
+ const component = mount(USelect, {
260
+ props: {
261
+ searchable: true,
262
+ options: defaultOptions,
263
+ },
264
+ attachTo: document.body,
265
+ });
266
+
267
+ await component.get("[role='combobox']").trigger("focus");
268
+ await flushPromises();
269
+
270
+ expect(component.emitted("open")).toBeTruthy();
271
+
272
+ const outside = document.createElement("button");
273
+
274
+ document.body.appendChild(outside);
275
+
276
+ await component
277
+ .getComponent(UListbox)
278
+ .get("input")
279
+ .trigger("blur", { relatedTarget: outside });
280
+ await flushPromises();
281
+
282
+ expect(component.emitted("close")).toBeTruthy();
283
+ expect(component.findComponent(UListbox).exists()).toBe(false);
284
+
285
+ outside.remove();
286
+ component.unmount();
287
+ });
288
+
289
+ it("Searchable – stays open when focus moves within its own listbox", async () => {
290
+ const component = mount(USelect, {
291
+ props: {
292
+ searchable: true,
293
+ options: defaultOptions,
294
+ },
295
+ attachTo: document.body,
296
+ });
297
+
298
+ await component.get("[role='combobox']").trigger("focus");
299
+ await flushPromises();
300
+
301
+ const listboxEl = component.getComponent(UListbox).element as HTMLElement;
302
+
303
+ await component
304
+ .getComponent(UListbox)
305
+ .get("input")
306
+ .trigger("blur", { relatedTarget: listboxEl });
307
+ await flushPromises();
308
+
309
+ expect(component.emitted("close")).toBeFalsy();
310
+ expect(component.findComponent(UListbox).exists()).toBe(true);
311
+
312
+ component.unmount();
313
+ });
314
+
258
315
  it("Search v-model – passes search to UListbox and filters", async () => {
259
316
  const component = mount(USelect, {
260
317
  props: {
@@ -6,7 +6,8 @@ export default /*tw*/ {
6
6
  },
7
7
  wrapper: {
8
8
  base: `
9
- flex items-center p-0.5 relative rounded-full outline-solid outline-transparent cursor-pointer transition focus-within:transition
9
+ flex items-center p-0.5 relative cursor-pointer transition focus-within:transition
10
+ rounded-full outline-solid outline-medium outline-transparent
10
11
  focus-visible:outline focus-visible:outline-medium focus-visible:outline-offset-2 focus-visible:outline-{color}
11
12
  `,
12
13
  variants: {
@@ -15,7 +15,7 @@ export default /*tw*/ {
15
15
  wrapper: {
16
16
  base: `
17
17
  flex px-3 py-2 gap-3 w-full bg-default transition
18
- rounded-medium border border-solid border-default outline-solid outline-transparent
18
+ rounded-medium border border-solid border-default outline-solid outline-small outline-transparent
19
19
  hover:border-lifted hover:focus-within:border-primary focus-within:border-primary
20
20
  focus-within:outline focus-within:outline-small focus-within:outline-primary focus-within:transition
21
21
  `,
@@ -2,7 +2,7 @@ export default /*tw*/ {
2
2
  badge: {
3
3
  base: `
4
4
  inline-flex items-center justify-between py-1
5
- border border-solid rounded-medium leading-none! outline-solid outline-transparent
5
+ border border-solid rounded-medium leading-none! outline-solid outline-medium outline-transparent
6
6
  `,
7
7
  variants: {
8
8
  variant: {