Skip to content

Commit 62b4525

Browse files
committed
Merge branch 'next' into current
2 parents 4462888 + afa69b4 commit 62b4525

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+617
-239
lines changed

cspell.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"autoplay",
1616
"bezier",
1717
"boxicons",
18+
"CACHEABLE",
1819
"callout",
1920
"callouts",
2021
"chatbubble",
@@ -111,6 +112,8 @@
111112
"reregister",
112113
"resizer",
113114
"resizers",
115+
"retargeted",
116+
"RETRYABLE",
114117
"rgba",
115118
"roadmap",
116119
"Roboto",

docs/frameworks/angular.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,38 @@ import { AppComponent } from './app.component';
4141
export class AppModule {}
4242
```
4343

44+
## Reference Shoelace components in your Angular component code
45+
46+
```js
47+
import { SlDrawer } from '@shoelace-style/shoelace';
48+
49+
@Component({
50+
selector: 'app-drawer-example',
51+
template: '<div id="page"><button (click)="showDrawer()">Show drawer</button><sl-drawer #drawer label="Drawer" class="drawer-focus" style="--size: 50vw"><p>Drawer content</p></sl-drawer></div>'
52+
})
53+
export class DrawerExampleComponent implements OnInit {
54+
55+
// use @ViewChild to get a reference to the #drawer element within component template
56+
@ViewChild('drawer')
57+
drawer?: ElementRef<SlDrawer>;
58+
59+
...
60+
61+
constructor(...) {
62+
}
63+
64+
ngOnInit() {
65+
}
66+
67+
...
68+
69+
showDrawer() {
70+
// use nativeElement to access Shoelace components
71+
this.drawer?.nativeElement.show();
72+
}
73+
}
74+
```
75+
4476
Now you can start using Shoelace components in your app!
4577
4678
?> Are you using Shoelace with Angular? [Help us improve this page!](https://github.com/shoelace-style/shoelace/blob/next/docs/frameworks/angular.md)

docs/frameworks/vue.md

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ Now you can start using Shoelace components in your app!
5858
5959
### QR code generator example
6060
61-
```vue
61+
```html
6262
<template>
6363
<div class="container">
6464
<h1>QR code generator</h1>
@@ -70,22 +70,22 @@ Now you can start using Shoelace components in your app!
7070
</template>
7171

7272
<script setup>
73-
import { ref } from 'vue';
74-
import '@shoelace-style/shoelace/dist/components/qr-code/qr-code.js';
75-
import '@shoelace-style/shoelace/dist/components/input/input.js';
73+
import { ref } from 'vue';
74+
import '@shoelace-style/shoelace/dist/components/qr-code/qr-code.js';
75+
import '@shoelace-style/shoelace/dist/components/input/input.js';
7676

77-
const qrCode = ref();
77+
const qrCode = ref();
7878
</script>
7979

8080
<style>
81-
.container {
82-
max-width: 400px;
83-
margin: 0 auto;
84-
}
85-
86-
sl-input {
87-
margin: var(--sl-spacing-large) 0;
88-
}
81+
.container {
82+
max-width: 400px;
83+
margin: 0 auto;
84+
}
85+
86+
sl-input {
87+
margin: var(--sl-spacing-large) 0;
88+
}
8989
</style>
9090
```
9191
@@ -98,3 +98,18 @@ When binding complex data such as objects and arrays, use the `.prop` modifier t
9898
```
9999
100100
?> Are you using Shoelace with Vue? [Help us improve this page!](https://github.com/shoelace-style/shoelace/blob/next/docs/frameworks/vue.md)
101+
102+
### Slots
103+
104+
To use Shoelace components with slots, follow the Vue documentation on using [slots with custom elements](https://vuejs.org/guide/extras/web-components.html#building-custom-elements-with-vue).
105+
106+
Here is an example:
107+
108+
```html
109+
<sl-drawer label="Drawer" placement="start" class="drawer-placement-start" :open="drawerIsOpen">
110+
This drawer slides in from the start.
111+
<div slot="footer">
112+
<sl-button variant="primary" @click=" drawerIsOpen = false">Close</sl-button>
113+
</div>
114+
</sl-drawer>
115+
```

docs/getting-started/usage.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ Refer to a component's documentation for a complete list of its properties.
3333

3434
## Events
3535

36-
You can listen for standard events such as `click`, `mouseover`, etc. as you normally would. In addition, some components emit custom events. These work the same way as standard events, but are prefixed with `sl-` to prevent collisions with standard events and other libraries.
36+
You can listen for standard events such as `click`, `mouseover`, etc. as you normally would. However, it's important to note that many events emitted within a component's shadow root will be [retargeted](https://dom.spec.whatwg.org/#retarget) to the host element. This may result in, for example, multiple `click` handlers executing even if the user clicks just once. Furthermore, `event.target` will point to the host element, making things even more confusing.
37+
38+
As a result, you should almost always listen for custom events instead. For example, instead of listening to `click` to determine when an `<sl-checkbox>` gets toggled, listen to `sl-change`.
3739

3840
```html
3941
<sl-checkbox>Check me</sl-checkbox>
@@ -46,7 +48,7 @@ You can listen for standard events such as `click`, `mouseover`, etc. as you nor
4648
</script>
4749
```
4850

49-
Refer to a component's documentation for a complete list of its custom events.
51+
All custom events are prefixed with `sl-` to prevent collisions with standard events and other libraries. Refer to a component's documentation for a complete list of its custom events.
5052

5153
## Methods
5254

docs/resources/changelog.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,26 @@ Components with the <sl-badge variant="warning" pill>Experimental</sl-badge> bad
66

77
New versions of Shoelace are released as-needed and generally occur when a critical mass of changes have accumulated. At any time, you can see what's coming in the next release by visiting [next.shoelace.style](https://next.shoelace.style).
88

9+
## 2.4.0
10+
11+
- Added the `discover()` function to the experimental autoloader's exports [#1236](https://github.com/shoelace-style/shoelace/pull/1236)
12+
- Added the `size` attribute to `<sl-radio-group>` so labels and controls will be sized consistently [#1301](https://github.com/shoelace-style/shoelace/issues/1301)
13+
- Added tests for `<sl-animated-image>` [#1246](https://github.com/shoelace-style/shoelace/pull/1246)
14+
- Added tests for `<sl-animation>` [#1274](https://github.com/shoelace-style/shoelace/pull/1274)
15+
- Fixed a bug in `<sl-tree-item>` that prevented long labels from wrapping [#1243](https://github.com/shoelace-style/shoelace/issues/1243)
16+
- Fixed a bug in `<sl-tree-item>` that caused labels to be misaligned when text wraps [#1244](https://github.com/shoelace-style/shoelace/issues/1244)
17+
- Fixed an incorrect CSS property value in `<sl-checkbox>` [#1272](https://github.com/shoelace-style/shoelace/pull/1272)
18+
- Fixed a bug in `<sl-avatar>` that caused the initials to show up behind images with transparency [#1260](https://github.com/shoelace-style/shoelace/pull/1260)
19+
- Fixed a bug in `<sl-split-panel>` that prevented the divider from being focusable in some browsers [#1288](https://github.com/shoelace-style/shoelace/issues/1288)
20+
- Fixed a bug that caused `<sl-tab-group>` to affect scrolling when initializing [#1292](https://github.com/shoelace-style/shoelace/issues/1292)
21+
- Fixed a bug in `<sl-menu-item>` that allowed the hover state to show when focused [#1282](https://github.com/shoelace-style/shoelace/issues/1282)
22+
- Fixed a bug in `<sl-carousel>` that prevented interactive elements from receiving clicks [#1262](https://github.com/shoelace-style/shoelace/issues/1262)
23+
- Fixed a bug in `<sl-input>` that caused `valueAsDate` and `valueAsNumber` to not be set synchronously in some cases [#1302](https://github.com/shoelace-style/shoelace/issues/1302)
24+
- Improved the behavior of `<sl-carousel>` when used inside a flex container [#1235](https://github.com/shoelace-style/shoelace/pull/1235)
25+
- Improved the behavior of `<sl-tree-item>` to support buttons and other interactive elements [#1234](https://github.com/shoelace-style/shoelace/issues/1234)
26+
- Improved the performance of `<sl-include>` to prevent an apparent memory leak in some browsers [#1284](https://github.com/shoelace-style/shoelace/pull/1284)
27+
- Improved the accessibility of `<sl-select>`, `<sl-split-panel>`, and `<sl-details>` by ensuring slots don't have roles [#1287](https://github.com/shoelace-style/shoelace/issues/1287)
28+
929
## 2.3.0
1030

1131
- Added an experimental autoloader
@@ -27,7 +47,7 @@ New versions of Shoelace are released as-needed and generally occur when a criti
2747
- Fixed a bug in `<sl-select>` that caused the display label to render incorrectly in Chrome after form validation [#1197](https://github.com/shoelace-style/shoelace/discussions/1197)
2848
- Fixed a bug in `<sl-input>` that prevented users from applying their own value for `autocapitalize`, `autocomplete`, and `autocorrect` when using `type="password` [#1205](https://github.com/shoelace-style/shoelace/issues/1205)
2949
- Fixed a bug in `<sl-tab-group>` that prevented scroll controls from showing when dynamically adding tabs [#1208](https://github.com/shoelace-style/shoelace/issues/1208)
30-
- Fixed a big in `<sl-input>` that caused the calendar icon to be clipped in Firefox [#1213](https://github.com/shoelace-style/shoelace/pull/1213)
50+
- Fixed a bug in `<sl-input>` that caused the calendar icon to be clipped in Firefox [#1213](https://github.com/shoelace-style/shoelace/pull/1213)
3151
- Fixed a bug in `<sl-tab>` that caused `sl-tab-show` to be emitted when activating the close button
3252
- Fixed a bug in `<sl-spinner>` that caused `--track-color` to be invisible with certain colors
3353
- Fixed a bug in `<sl-menu-item>` that caused the focus color to show when selecting menu items with a mouse or touch device

docs/resources/contributing.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,3 +363,26 @@ Avoid inlining SVG icons inside of templates. If a component requires an icon, m
363363
```
364364

365365
This will render the icons instantly whereas the default library will fetch them from a remote source. If an icon isn't available in the system library, you will need to add it to `library.system.ts`. Using the system library ensures that all icons load instantly and are customizable by users who wish to provide a custom resolver for the system library.
366+
367+
### Writing tests
368+
369+
What to test for a given component:
370+
371+
- Start with a simple test that checks that the default version of the component still renders.
372+
- Add at least one accessibility test (The accessibility check only covers the parts of the DOM which are currently visible and rendered. Depending on the component, more than one accessibility test is required to cover all scenarios.):
373+
374+
```ts
375+
const myComponent = await fixture<SlAlert>(html`<sl-my-component>SomeContent</sl-my-component>`);
376+
377+
await expect(myComponent).to.be.accessible();
378+
```
379+
380+
- Try to cover all features advertised in the component's description
381+
382+
Guidelines for writing tests:
383+
384+
- Each test should declare its own, hand crafted hml fixture for the component. Do not try to write one big component to match all tests. This helps keeping each test understandable in isolation.
385+
- Tests should not produce log lines. Note that sometimes this cannot be prevented as the test runner might log errors (e.g. 404s).
386+
- Try keeping the main test readable: Extract more complicated sets of selectors/commands/assertions into separate functions.
387+
- Try to aim testing the user facing features of the component instead of the internal workings of the component.
388+
- Group multiple tests for one feature into describe blocks.

package-lock.json

Lines changed: 2 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@shoelace-style/shoelace",
33
"description": "A forward-thinking library of web components.",
4-
"version": "2.3.0",
4+
"version": "2.4.0",
55
"homepage": "https://github.com/shoelace-style/shoelace",
66
"author": "Cory LaViska",
77
"license": "MIT",
@@ -110,7 +110,6 @@
110110
"lint-staged": "^13.1.0",
111111
"lunr": "^2.3.9",
112112
"npm-check-updates": "^16.6.2",
113-
"open": "^8.4.0",
114113
"pascal-case": "^3.1.2",
115114
"plop": "^3.1.1",
116115
"prettier": "^2.8.2",

scripts/build.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import esbuild from 'esbuild';
77
import fs from 'fs';
88
import getPort, { portNumbers } from 'get-port';
99
import { globby } from 'globby';
10-
import open from 'open';
1110
import copy from 'recursive-copy';
1211

1312
const { bundle, copydir, dir, serve, types } = commandLineArgs([
@@ -108,7 +107,6 @@ fs.mkdirSync(outdir, { recursive: true });
108107
deleteSync('docs/dist');
109108

110109
const browserSyncConfig = {
111-
open: false,
112110
startPath: '/',
113111
port,
114112
logLevel: 'silent',
@@ -145,7 +143,6 @@ fs.mkdirSync(outdir, { recursive: true });
145143
bs.init(browserSyncConfig, () => {
146144
const url = `http://localhost:${port}`;
147145
console.log(chalk.cyan(`Launched the Shoelace dev server at ${url} 🥾\n`));
148-
open(url);
149146
});
150147

151148
// Rebuild and reload when source files change
Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,70 @@
1-
import { expect, fixture, html } from '@open-wc/testing';
1+
import { clickOnElement } from '../../internal/test';
2+
import { expect, fixture, html, oneEvent } from '@open-wc/testing';
3+
import type SlAnimatedImage from './animated-image';
24

35
describe('<sl-animated-image>', () => {
46
it('should render a component', async () => {
5-
const el = await fixture(html` <sl-animated-image></sl-animated-image> `);
7+
const animatedImage = await fixture(html` <sl-animated-image></sl-animated-image> `);
68

7-
expect(el).to.exist;
9+
expect(animatedImage).to.exist;
10+
});
11+
12+
it('should render be accessible', async () => {
13+
const animatedImage = await fixture(html` <sl-animated-image></sl-animated-image> `);
14+
15+
await expect(animatedImage).to.be.accessible();
16+
});
17+
18+
const files = ['docs/assets/images/walk.gif', 'docs/assets/images/tie.webp'];
19+
20+
files.forEach((file: string) => {
21+
it(`should load a ${file} without errors`, async () => {
22+
const animatedImage = await fixture<SlAnimatedImage>(html` <sl-animated-image></sl-animated-image> `);
23+
let errorCount = 0;
24+
oneEvent(animatedImage, 'sl-error').then(() => errorCount++);
25+
await loadImage(animatedImage, file);
26+
27+
expect(errorCount).to.be.equal(0);
28+
});
29+
30+
it(`should play ${file} on click`, async () => {
31+
const animatedImage = await fixture<SlAnimatedImage>(html` <sl-animated-image></sl-animated-image> `);
32+
await loadImage(animatedImage, file);
33+
34+
expect(animatedImage.play).not.to.be.true;
35+
36+
await clickOnElement(animatedImage);
37+
38+
expect(animatedImage.play).to.be.true;
39+
});
40+
41+
it(`should pause and resume ${file} on click`, async () => {
42+
const animatedImage = await fixture<SlAnimatedImage>(html` <sl-animated-image></sl-animated-image> `);
43+
await loadImage(animatedImage, file);
44+
45+
animatedImage.play = true;
46+
47+
await clickOnElement(animatedImage);
48+
49+
expect(animatedImage.play).to.be.false;
50+
51+
await clickOnElement(animatedImage);
52+
53+
expect(animatedImage.play).to.be.true;
54+
});
55+
});
56+
57+
it('should emit an error event on invalid url', async () => {
58+
const animatedImage = await fixture<SlAnimatedImage>(html` <sl-animated-image></sl-animated-image> `);
59+
60+
const errorPromise = oneEvent(animatedImage, 'sl-error');
61+
animatedImage.src = 'completelyWrong';
62+
63+
await errorPromise;
864
});
965
});
66+
async function loadImage(animatedImage: SlAnimatedImage, file: string) {
67+
const loadingPromise = oneEvent(animatedImage, 'sl-load');
68+
animatedImage.src = file;
69+
await loadingPromise;
70+
}

0 commit comments

Comments
 (0)