Compare commits

...

3 Commits

Author SHA1 Message Date
Cory LaViska
8cf1171a65 cleanup /index.html from search results 2023-07-13 15:21:37 -04:00
Cory LaViska
414197acc9 unset last focused item; #1436 (#1446) 2023-07-12 14:52:13 -04:00
Ben Anderson
8fd01e1eda Add event types to react wrapper components (#1419)
* Rename SlSlideChange for consistency with other events

* Setup React event types for events used by Shoelace components

Means that consumers of Shoelace via the React wrapper will be able to
use callback methods with the correct event type, instead of having to
rely on casting and friends when using Typescript.

* Add docs demonstrating importing event types for React callbacks
2023-07-12 11:31:27 -04:00
8 changed files with 46 additions and 8 deletions

View File

@@ -116,6 +116,7 @@ export default {
if (classDoc?.events) {
classDoc.events.forEach(event => {
event.reactName = `on${pascalCase(event.name)}`;
event.eventName = `${pascalCase(event.name)}Event`;
});
}
}

View File

@@ -284,7 +284,7 @@
const a = document.createElement('a');
const displayTitle = page.title ?? '';
const displayDescription = page.description ?? '';
const displayUrl = page.url.replace(/^\//, '');
const displayUrl = page.url.replace(/^\//, '').replace(/\/$/, '');
let icon = 'file-text';
a.setAttribute('role', 'option');

View File

@@ -171,7 +171,10 @@ module.exports = function (eleventyConfig) {
this.field('c'); // content
results.forEach((result, index) => {
const url = path.join('/', path.relative(eleventyConfig.dir.output, result.outputPath)).replace(/\\/g, '/');
const url = path
.join('/', path.relative(eleventyConfig.dir.output, result.outputPath))
.replace(/\\/g, '/') // convert backslashes to forward slashes
.replace(/\/index.html$/, '/'); // convert trailing /index.html to /
const doc = new JSDOM(result.content, {
// We must set a default URL so links are parsed with a hostname. Let's use a bogus TLD so we can easily
// identify which ones are internal and which ones are external.

View File

@@ -83,6 +83,25 @@ function MyComponent() {
export default MyComponent;
```
You can also import the event type for use in your callbacks, shown below.
```tsx
import { useCallback, useState } from 'react';
import { SlInput, SlInputEvent } from '@shoelace-style/shoelace/%NPMDIR%/react';
import type SlInputElement from '@shoelace-style/shoelace/%NPMDIR%/components/input/input';
function MyComponent() {
const [value, setValue] = useState('');
const onInput = useCallback((event: SlInputEvent) => {
setValue(event.detail);
}, []);
return <SlInput value={value} onSlInput={event => setValue((event.target as SlInputElement).value)} />;
}
export default MyComponent;
```
## Testing with Jest
Testing with web components can be challenging if your test environment runs in a Node environment (i.e. it doesn't run in a real browser). Fortunately, [Jest](https://jestjs.io/) has made a number of strides to support web components and provide additional browser APIs. However, it's still not a complete replication of a browser environment.

View File

@@ -25,7 +25,14 @@ components.map(component => {
const componentDir = path.join(reactDir, tagWithoutPrefix);
const componentFile = path.join(componentDir, 'index.ts');
const importPath = component.path;
const events = (component.events || []).map(event => `${event.reactName}: '${event.name}'`).join(',\n');
const eventImports = (component.events || [])
.map(event => `import { ${event.eventName} } from '../../../src/events/events';`)
.join('\n');
const eventNameImport =
(component.events || []).length > 0 ? `import { type EventName } from '@lit-labs/react';` : ``;
const events = (component.events || [])
.map(event => `${event.reactName}: '${event.name}' as EventName<${event.eventName}>`)
.join(',\n');
fs.mkdirSync(componentDir, { recursive: true });
@@ -35,6 +42,9 @@ components.map(component => {
import { createComponent } from '@lit-labs/react';
import Component from '../../${importPath}';
${eventNameImport}
${eventImports}
export default createComponent({
tagName: '${component.tagName}',
elementClass: Component,

View File

@@ -87,7 +87,7 @@ export default class SlTree extends ShoelaceElement {
// A collection of all the items in the tree, in the order they appear. The collection is live, meaning it is
// automatically updated when the underlying document is changed.
//
private lastFocusedItem: SlTreeItem;
private lastFocusedItem: SlTreeItem | null;
private readonly localize = new LocalizeController(this);
private mutationObserver: MutationObserver;
private clickTarget: SlTreeItem | null = null;
@@ -159,8 +159,13 @@ export default class SlTree extends ShoelaceElement {
private handleTreeChanged = (mutations: MutationRecord[]) => {
for (const mutation of mutations) {
const addedNodes: SlTreeItem[] = [...mutation.addedNodes].filter(SlTreeItem.isTreeItem) as SlTreeItem[];
const removedNodes = [...mutation.removedNodes].filter(SlTreeItem.isTreeItem) as SlTreeItem[];
addedNodes.forEach(this.initTreeItem);
if (this.lastFocusedItem && removedNodes.includes(this.lastFocusedItem)) {
this.lastFocusedItem = null;
}
}
};

View File

@@ -28,7 +28,7 @@ export type { default as SlResizeEvent } from './sl-resize';
export type { default as SlSelectEvent } from './sl-select';
export type { default as SlSelectionChangeEvent } from './sl-selection-change';
export type { default as SlShowEvent } from './sl-show';
export type { default as SlSlideChange } from './sl-slide-change';
export type { default as SlSlideChangeEvent } from './sl-slide-change';
export type { default as SlStartEvent } from './sl-start';
export type { default as SlTabHideEvent } from './sl-tab-hide';
export type { default as SlTabShowEvent } from './sl-tab-show';

View File

@@ -1,11 +1,11 @@
import type SlCarouselItem from '../components/carousel-item/carousel-item';
type SlSlideChange = CustomEvent<{ index: number; slide: SlCarouselItem }>;
type SlSlideChangeEvent = CustomEvent<{ index: number; slide: SlCarouselItem }>;
declare global {
interface GlobalEventHandlersEventMap {
'sl-slide-change': SlSlideChange;
'sl-slide-change': SlSlideChangeEvent;
}
}
export default SlSlideChange;
export default SlSlideChangeEvent;