Merge branch 'next' into efficient-style-imports

pull/1861/head
Cory LaViska 2024-02-09 10:12:31 -05:00
commit cfe836a1a3
5 zmienionych plików z 97 dodań i 35 usunięć

Wyświetl plik

@ -14,13 +14,15 @@ New versions of Shoelace are released as-needed and generally occur when a criti
## Next
- Added the Arabic translation [#1852]
- Added help text to `<sl-checkbox>` [#1860]
- Added help text to `<sl-switch>` [#1800]
- Fixed a bug in `<sl-option>` that caused HTML tags to be included in `getTextLabel()`
- Fixed a bug in `<sl-carousel>` that caused slides to not switch correctly [#1862]
- Refactored component styles to be consumed more efficiently [#1692]
## 2.13.1
- Added help text to `<sl-checkbox>`
- Added help text to `<sl-switch>` [#1800]
- Fixed a bug where the safe triangle was always visible when selecting nested `<sl-menu>` elements [#1835]
## 2.13.0

Wyświetl plik

@ -477,7 +477,7 @@ export default class SlCarousel extends ShoelaceElement {
this.scrollToSlide(nextSlide, prefersReducedMotion() ? 'auto' : behavior);
}
private async scrollToSlide(slide: HTMLElement, behavior: ScrollBehavior = 'smooth') {
private scrollToSlide(slide: HTMLElement, behavior: ScrollBehavior = 'smooth') {
const scrollContainer = this.scrollContainer;
const scrollContainerRect = scrollContainer.getBoundingClientRect();
const nextSlideRect = slide.getBoundingClientRect();
@ -485,16 +485,11 @@ export default class SlCarousel extends ShoelaceElement {
const nextLeft = nextSlideRect.left - scrollContainerRect.left;
const nextTop = nextSlideRect.top - scrollContainerRect.top;
// If the slide is already in view, don't need to scroll
if (nextLeft !== scrollContainer.scrollLeft || nextTop !== scrollContainer.scrollTop) {
scrollContainer.scrollTo({
left: nextLeft + scrollContainer.scrollLeft,
top: nextTop + scrollContainer.scrollTop,
behavior
});
await waitForEvent(scrollContainer, 'scrollend');
}
scrollContainer.scrollTo({
left: nextLeft + scrollContainer.scrollLeft,
top: nextTop + scrollContainer.scrollTop,
behavior
});
}
render() {

Wyświetl plik

@ -1,6 +1,6 @@
import '../../../dist/shoelace.js';
import { clickOnElement, dragElement, moveMouseOnElement } from '../../internal/test.js';
import { expect, fixture, html, oneEvent } from '@open-wc/testing';
import { expect, fixture, html, nextFrame, oneEvent } from '@open-wc/testing';
import { map } from 'lit/directives/map.js';
import { range } from 'lit/directives/range.js';
import { resetMouse } from '@web/test-runner-commands';
@ -8,10 +8,16 @@ import sinon from 'sinon';
import type SlCarousel from './carousel.js';
describe('<sl-carousel>', () => {
const sandbox = sinon.createSandbox();
afterEach(async () => {
await resetMouse();
});
afterEach(() => {
sandbox.restore();
});
it('should render a carousel with default configuration', async () => {
// Arrange
const el = await fixture(html`
@ -34,15 +40,11 @@ describe('<sl-carousel>', () => {
let clock: sinon.SinonFakeTimers;
beforeEach(() => {
clock = sinon.useFakeTimers({
clock = sandbox.useFakeTimers({
now: new Date()
});
});
afterEach(() => {
clock.restore();
});
it('should scroll forwards every `autoplay-interval` milliseconds', async () => {
// Arrange
const el = await fixture<SlCarousel>(html`
@ -52,7 +54,7 @@ describe('<sl-carousel>', () => {
<sl-carousel-item>Node 3</sl-carousel-item>
</sl-carousel>
`);
sinon.stub(el, 'next');
sandbox.stub(el, 'next');
await el.updateComplete;
@ -73,7 +75,7 @@ describe('<sl-carousel>', () => {
<sl-carousel-item>Node 3</sl-carousel-item>
</sl-carousel>
`);
sinon.stub(el, 'next');
sandbox.stub(el, 'next');
await el.updateComplete;
@ -96,7 +98,7 @@ describe('<sl-carousel>', () => {
<sl-carousel-item>Node 3</sl-carousel-item>
</sl-carousel>
`);
sinon.stub(el, 'next');
sandbox.stub(el, 'next');
await el.updateComplete;
@ -183,7 +185,7 @@ describe('<sl-carousel>', () => {
<sl-carousel-item>Node 3</sl-carousel-item>
</sl-carousel>
`);
sinon.stub(el, 'goToSlide');
sandbox.stub(el, 'goToSlide');
await el.updateComplete;
// Act
@ -473,7 +475,7 @@ describe('<sl-carousel>', () => {
</sl-carousel>
`);
const nextButton: HTMLElement = el.shadowRoot!.querySelector('.carousel__navigation-button--next')!;
sinon.stub(el, 'next');
sandbox.stub(el, 'next');
await el.updateComplete;
@ -496,7 +498,7 @@ describe('<sl-carousel>', () => {
</sl-carousel>
`);
const nextButton: HTMLElement = el.shadowRoot!.querySelector('.carousel__navigation-button--next')!;
sinon.stub(el, 'next');
sandbox.stub(el, 'next');
el.goToSlide(2, 'auto');
await oneEvent(el.scrollContainer, 'scrollend');
@ -560,7 +562,7 @@ describe('<sl-carousel>', () => {
await el.updateComplete;
const previousButton: HTMLElement = el.shadowRoot!.querySelector('.carousel__navigation-button--previous')!;
sinon.stub(el, 'previous');
sandbox.stub(el, 'previous');
await el.updateComplete;
@ -584,7 +586,7 @@ describe('<sl-carousel>', () => {
`);
const previousButton: HTMLElement = el.shadowRoot!.querySelector('.carousel__navigation-button--previous')!;
sinon.stub(el, 'previous');
sandbox.stub(el, 'previous');
await el.updateComplete;
// Act
@ -632,19 +634,27 @@ describe('<sl-carousel>', () => {
it('should scroll the carousel to the next slide', async () => {
// Arrange
const el = await fixture<SlCarousel>(html`
<sl-carousel slides-per-page="2" slides-per-move="2">
<sl-carousel>
<sl-carousel-item>Node 1</sl-carousel-item>
<sl-carousel-item>Node 2</sl-carousel-item>
<sl-carousel-item>Node 3</sl-carousel-item>
</sl-carousel>
`);
sinon.stub(el, 'goToSlide');
await el.updateComplete;
sandbox.spy(el, 'goToSlide');
const expectedCarouselItem: HTMLElement = el.querySelector('sl-carousel-item:nth-child(2)')!;
// Act
el.next();
await oneEvent(el.scrollContainer, 'scrollend');
await el.updateComplete;
expect(el.goToSlide).to.have.been.calledWith(2);
const containerRect = el.scrollContainer.getBoundingClientRect();
const itemRect = expectedCarouselItem.getBoundingClientRect();
// Assert
expect(el.goToSlide).to.have.been.calledWith(1);
expect(itemRect.top).to.be.equal(containerRect.top);
expect(itemRect.left).to.be.equal(containerRect.left);
});
});
@ -652,19 +662,32 @@ describe('<sl-carousel>', () => {
it('should scroll the carousel to the previous slide', async () => {
// Arrange
const el = await fixture<SlCarousel>(html`
<sl-carousel slides-per-page="2" slides-per-move="2">
<sl-carousel>
<sl-carousel-item>Node 1</sl-carousel-item>
<sl-carousel-item>Node 2</sl-carousel-item>
<sl-carousel-item>Node 3</sl-carousel-item>
</sl-carousel>
`);
sinon.stub(el, 'goToSlide');
await el.updateComplete;
const expectedCarouselItem: HTMLElement = el.querySelector('sl-carousel-item:nth-child(1)')!;
el.goToSlide(1);
await oneEvent(el.scrollContainer, 'scrollend');
await nextFrame();
sandbox.spy(el, 'goToSlide');
// Act
el.previous();
await oneEvent(el.scrollContainer, 'scrollend');
expect(el.goToSlide).to.have.been.calledWith(-2);
const containerRect = el.scrollContainer.getBoundingClientRect();
const itemRect = expectedCarouselItem.getBoundingClientRect();
// Assert
expect(el.goToSlide).to.have.been.calledWith(0);
expect(itemRect.top).to.be.equal(containerRect.top);
expect(itemRect.left).to.be.equal(containerRect.left);
});
});

Wyświetl plik

@ -228,6 +228,7 @@ export default class SlCheckbox extends ShoelaceElement implements ShoelaceFormC
.disabled=${this.disabled}
.required=${this.required}
aria-checked=${this.checked ? 'true' : 'false'}
aria-describedby="help-text"
@click=${this.handleClick}
@input=${this.handleInput}
@invalid=${this.handleInvalid}

Wyświetl plik

@ -0,0 +1,41 @@
import { registerTranslation } from '@shoelace-style/localize';
import type { Translation } from '../utilities/localize.js';
const translation: Translation = {
$code: 'ar',
$name: 'العربية',
$dir: 'rtl',
carousel: 'كاروسيل',
clearEntry: 'حذف الخيارات',
close: 'اغلاق',
copied: 'تم النسخ',
copy: 'نسخ',
currentValue: 'القيمة الحالية',
error: 'خطأ',
goToSlide: (slide, count) => `عرض شريحة رقم ${slide} من ${count}`,
hidePassword: 'اخفاء كلمة المرور',
loading: 'جاري التحميل',
nextSlide: 'الشريحة التالية',
numOptionsSelected: num => {
if (num === 0) return 'لم يتم تحديد أي خيارات';
if (num === 1) return 'تم تحديد خيار واحد';
if (num === 2) return 'تم تحديد خياران';
if (num > 2 && num < 11) return `تم تحديد ${num} خيارات`;
return `تم تحديد ${num} خيار`;
},
previousSlide: 'الشريحة السابقة',
progress: 'مقدار التقدم',
remove: 'حذف',
resize: 'تغيير الحجم',
scrollToEnd: 'الانتقال الى النهاية',
scrollToStart: 'الانتقال الى البداية',
selectAColorFromTheScreen: 'اختر لون من الشاشة',
showPassword: 'عرض كلمة المرور',
slideNum: slide => `شريحة ${slide}`,
toggleColorFormat: 'تغيير صيغة عرض اللون'
};
registerTranslation(translation);
export default translation;