mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-12 04:09:12 +00:00
Merge branch 'next' of https://github.com/shoelace-style/shoelace into konnorrogers/fix-value-as-number
This commit is contained in:
@@ -9,3 +9,4 @@ src/react/index.ts
|
||||
node_modules
|
||||
package-lock.json
|
||||
tsconfig.json
|
||||
cdn
|
||||
|
||||
@@ -34,9 +34,9 @@
|
||||
<meta property="og:image" content="{{ assetUrl(meta.image, true) }}" />
|
||||
|
||||
{# Shoelace #}
|
||||
<link rel="stylesheet" href="/cdn/themes/light.css" />
|
||||
<link rel="stylesheet" href="/cdn/themes/dark.css" />
|
||||
<script type="module" src="/cdn/shoelace-autoloader.js"></script>
|
||||
<link rel="stylesheet" href="/dist/themes/light.css" />
|
||||
<link rel="stylesheet" href="/dist/themes/dark.css" />
|
||||
<script type="module" src="/dist/shoelace-autoloader.js"></script>
|
||||
|
||||
{# Set the initial theme and menu states here to prevent flashing #}
|
||||
<script>
|
||||
|
||||
@@ -50,6 +50,8 @@
|
||||
|
||||
const shoelaceVersion = document.documentElement.getAttribute('data-shoelace-version');
|
||||
const reactVersion = '18.2.0';
|
||||
const cdndir = 'cdn';
|
||||
const npmdir = 'dist';
|
||||
let flavor = getFlavor();
|
||||
let count = 1;
|
||||
|
||||
@@ -180,7 +182,7 @@
|
||||
// HTML templates
|
||||
if (!isReact) {
|
||||
htmlTemplate =
|
||||
`<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${shoelaceVersion}/%CDNDIR%/shoelace.js"></script>\n` +
|
||||
`<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${shoelaceVersion}/${cdndir}/shoelace.js"></script>\n` +
|
||||
`\n${htmlExample}`;
|
||||
jsTemplate = '';
|
||||
}
|
||||
@@ -191,10 +193,10 @@
|
||||
jsTemplate =
|
||||
`import React from 'https://cdn.skypack.dev/react@${reactVersion}';\n` +
|
||||
`import ReactDOM from 'https://cdn.skypack.dev/react-dom@${reactVersion}';\n` +
|
||||
`import { setBasePath } from 'https://cdn.skypack.dev/@shoelace-style/shoelace@${shoelaceVersion}/%CDNDIR%/utilities/base-path';\n` +
|
||||
`import { setBasePath } from 'https://cdn.skypack.dev/@shoelace-style/shoelace@${shoelaceVersion}/${cdndir}/utilities/base-path';\n` +
|
||||
`\n` +
|
||||
`// Set the base path for Shoelace assets\n` +
|
||||
`setBasePath('https://cdn.skypack.dev/@shoelace-style/shoelace@${shoelaceVersion}/%NPMDIR%/')\n` +
|
||||
`setBasePath('https://cdn.skypack.dev/@shoelace-style/shoelace@${shoelaceVersion}/${npmdir}/')\n` +
|
||||
`\n${convertModuleLinks(reactExample)}\n` +
|
||||
`\n` +
|
||||
`ReactDOM.render(<App />, document.getElementById('root'));`;
|
||||
@@ -202,7 +204,7 @@
|
||||
|
||||
// CSS templates
|
||||
cssTemplate =
|
||||
`@import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${shoelaceVersion}/%CDNDIR%/themes/${
|
||||
`@import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${shoelaceVersion}/${cdndir}/themes/${
|
||||
isDark ? 'dark' : 'light'
|
||||
}.css';\n` +
|
||||
'\n' +
|
||||
|
||||
@@ -634,6 +634,39 @@ This example will load the same set of icons from the jsDelivr CDN instead of yo
|
||||
</script>
|
||||
```
|
||||
|
||||
#### Customize the default library to use SVG sprites
|
||||
|
||||
To improve performance you can use a SVG sprites to avoid multiple trips for each SVG. The browser will load the sprite sheet once and then you reference the particular SVG within the sprite sheet using hash selector.
|
||||
|
||||
As always, make sure to benchmark these changes. When using HTTP/2, it may in fact be more bandwidth-friendly to use multiple small requests instead of 1 large sprite sheet.
|
||||
|
||||
:::danger
|
||||
When using sprite sheets, the `sl-load` and `sl-error` events will not fire.
|
||||
:::
|
||||
|
||||
:::danger
|
||||
For security reasons, browsers may apply the same-origin policy on `<use>` elements located in the `<sl-icon>` shadow dom and
|
||||
may refuse to load a cross-origin URL. There is currently no defined way to set a cross-origin policy for `<use>` elements.
|
||||
For this reason, sprite sheets should only be used if you're self-hosting them.
|
||||
:::
|
||||
|
||||
```html:preview
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
|
||||
|
||||
registerIconLibrary('sprite', {
|
||||
resolver: name => `/assets/images/sprite.svg#${name}`,
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor'),
|
||||
spriteSheet: true
|
||||
});
|
||||
</script>
|
||||
|
||||
<div style="font-size: 24px;">
|
||||
<sl-icon library="sprite" name="clock"></sl-icon>
|
||||
<sl-icon library="sprite" name="speedometer"></sl-icon>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Customizing the System Library
|
||||
|
||||
The system library contains only the icons used internally by Shoelace components. Unlike the default icon library, the system library does not rely on physical assets. Instead, its icons are hard-coded as data URIs into the resolver to ensure their availability.
|
||||
@@ -666,7 +699,7 @@ If you want to change the icons Shoelace uses internally, you can register an ic
|
||||
}
|
||||
|
||||
fetch('/dist/assets/icons/icons.json')
|
||||
.then(res => res.json())
|
||||
.then(res => res.json())
|
||||
.then(icons => {
|
||||
const container = document.querySelector('.icon-search');
|
||||
const input = container.querySelector('sl-input');
|
||||
@@ -685,12 +718,12 @@ If you want to change the icons Shoelace uses internally, you can register an ic
|
||||
item.setAttribute('data-terms', [i.name, i.title, ...(i.tags || []), ...(i.categories || [])].join(' '));
|
||||
item.innerHTML = `
|
||||
<svg width="1em" height="1em" fill="currentColor">
|
||||
<use xlink:href="/assets/images/sprite.svg#${i.name}"></use>
|
||||
</svg>
|
||||
<use href="/assets/images/sprite.svg#${i.name}"></use>
|
||||
</svg>
|
||||
`;
|
||||
list.appendChild(item);
|
||||
|
||||
// Wrap it with a tooltip the first time the mouse lands on it. We do this instead of baking them into the DOM
|
||||
// Wrap it with a tooltip the first time the mouse lands on it. We do this instead of baking them into the DOM
|
||||
// to improve this page's performance. See: https://github.com/shoelace-style/shoelace/issues/1122
|
||||
item.addEventListener('mouseover', () => wrapWithTooltip(item), { once: true });
|
||||
|
||||
@@ -833,6 +866,6 @@ If you want to change the icons Shoelace uses internally, you can register an ic
|
||||
@media screen and (max-width: 500px) {
|
||||
.icon-list {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -108,9 +108,9 @@ Custom elements cannot have self-closing tags. Similar to `<script>` and `<texta
|
||||
|
||||
## Differences from Native Elements
|
||||
|
||||
You might expect similarly named elements to share the same API as native HTML elements. This is not always the case. Shoelace components **are not** designed to be one-to-one replacements for their HTML counterparts.
|
||||
You might expect similarly named elements to share the same API as native HTML elements, but this is not always the case. Shoelace components **are not** designed to be one-to-one replacements for their HTML counterparts. While they usually share the same API, there may be subtle differences.
|
||||
|
||||
For example, `<button>` and `<sl-button>` both have a `type` attribute, but it does different things. The former controls whether the button submits a form and the latter controls the button's appearance.
|
||||
For example, `<button>` and `<sl-button>` both have a `type` attribute, but the native one defaults to `submit` while the Shoelace one defaults to `button` since this is a better default for most users.
|
||||
|
||||
:::tip
|
||||
**Don't make assumptions about a component's API!** To prevent unexpected behaviors, please take the time to review the documentation and make sure you understand what each attribute, property, method, and event is intended to do.
|
||||
|
||||
@@ -26,11 +26,14 @@ If you're a CDN user, you must update your path to point to `cdn/` instead of `d
|
||||
- Added a `cdn/` distribution for bundled dependencies (imports for npm users remain the same) [#1369](https://github.com/shoelace-style/shoelace/pull/1369)
|
||||
- Added the `checkbox` part and related exported parts to `<sl-tree-item>` so you can target it with CSS [#1318](https://github.com/shoelace-style/shoelace/discussions/1318)
|
||||
- Added the `submenu-icon` part to `<sl-menu-item>` (submenus have not been implemented yet, but this part is required to allow customizations)
|
||||
- Added the ability to use Sprite Sheets when using `<sl-icon>` via a custom resolver.
|
||||
- Added tests for `<sl-split-panel>` [#1343](https://github.com/shoelace-style/shoelace/pull/1343)
|
||||
- Fixed a bug where changing the size of `<sl-radio-group>` wouldn't update the size of child elements
|
||||
- Fixed a bug in `<sl-select>` and `<sl-color-picker>` where the `size` attribute wasn't being reflected [#1318](https://github.com/shoelace-style/shoelace/issues/1348)
|
||||
- Fixed a bug in `<sl-radio-group>` where `<sl-radio>` would not get checked if `<sl-radio-group>` was defined first. [#1364](https://github.com/shoelace-style/shoelace/pull/1364/)
|
||||
- Fixed a bug in `<sl-input>` that caused date pickers to look filled in even when empty in Safari [#1341](https://github.com/shoelace-style/shoelace/pull/1341)
|
||||
- Fixed a bug in `<sl-radio-group>` that sometimes caused dual scrollbars in containers that overflowed [#1380](https://github.com/shoelace-style/shoelace/issues/1380)
|
||||
- Fixed a bug in `<sl-carousel>` not loading the English language pack automatically. [#1384](https://github.com/shoelace-style/shoelace/pull/1384)
|
||||
- Improved `<sl-button>` so it can accept children of variable heights [#1317](https://github.com/shoelace-style/shoelace/pull/1317)
|
||||
- Improved the docs to more clearly explain sizing radios and radio buttons
|
||||
- Improved the performance of `<sl-rating>` by partially rendering unseen icons [#1310](https://github.com/shoelace-style/shoelace/pull/1310)
|
||||
|
||||
@@ -70,11 +70,6 @@ async function buildTheDocs(watch = false) {
|
||||
//
|
||||
async function buildTheSource() {
|
||||
const alwaysExternal = ['@lit-labs/react', 'react'];
|
||||
const packageJSON = await fs.readFile('./package.json');
|
||||
const dependencies = [
|
||||
...Object.keys(packageJSON.dependencies || {}),
|
||||
...Object.keys(packageJSON.peerDependencies || {})
|
||||
];
|
||||
|
||||
const cdnConfig = {
|
||||
format: 'esm',
|
||||
@@ -215,8 +210,9 @@ if (!serve) {
|
||||
await nextTask(`Copying the build to "${sitedir}"`, async () => {
|
||||
await deleteAsync(sitedir);
|
||||
|
||||
// We copy the CDN build because that has everything bundled.
|
||||
await copy(cdndir, path.join(sitedir, 'cdn'));
|
||||
// We copy the CDN build because that has everything bundled. Yes this looks weird.
|
||||
// But if we do "/cdn" it requires changes all the docs to do /cdn instead of /dist.
|
||||
await copy(cdndir, path.join(sitedir, 'dist'));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -245,7 +241,7 @@ if (serve) {
|
||||
server: {
|
||||
baseDir: sitedir,
|
||||
routes: {
|
||||
'/cdn': './cdn'
|
||||
'/dist': './cdn'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import '../../../dist/shoelace.js';
|
||||
import { expect, fixture, html } from '@open-wc/testing';
|
||||
|
||||
describe('<{{ tag }}>', () => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { clamp } from '../../internal/math';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
||||
import { html } from 'lit';
|
||||
import { LocalizeController } from '@shoelace-style/localize';
|
||||
import { LocalizeController } from '../../utilities/localize';
|
||||
import { map } from 'lit/directives/map.js';
|
||||
import { prefersReducedMotion } from '../../internal/animate';
|
||||
import { range } from 'lit/directives/range.js';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { elementUpdated, expect, fixture, html, oneEvent } from '@open-wc/testing';
|
||||
import { aTimeout, elementUpdated, expect, fixture, html, oneEvent } from '@open-wc/testing';
|
||||
import { registerIconLibrary } from '../../../dist/shoelace.js';
|
||||
import type SlErrorEvent from '../../events/sl-error';
|
||||
import type SlIcon from './icon';
|
||||
@@ -154,4 +154,79 @@ describe('<sl-icon>', () => {
|
||||
expect(ev).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('svg spritesheets', () => {
|
||||
// For some reason ESLint wants to fail in CI here, but works locally.
|
||||
/* eslint-disable */
|
||||
it('Should properly grab an SVG and render it from bootstrap icons', async () => {
|
||||
registerIconLibrary('sprite', {
|
||||
resolver: name => `/docs/assets/images/sprite.svg#${name}`,
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor'),
|
||||
spriteSheet: true
|
||||
});
|
||||
|
||||
const el = await fixture<SlIcon>(html`<sl-icon name="arrow-left" library="sprite"></sl-icon>`);
|
||||
|
||||
await elementUpdated(el);
|
||||
|
||||
const svg = el.shadowRoot?.querySelector("svg[part='svg']");
|
||||
const use = svg?.querySelector(`use[href='/docs/assets/images/sprite.svg#arrow-left']`);
|
||||
|
||||
expect(svg).to.be.instanceof(SVGElement);
|
||||
expect(use).to.be.instanceof(SVGUseElement);
|
||||
|
||||
// This is kind of hacky...but with no way to check "load", we just do a timeout :shrug:
|
||||
await aTimeout(200);
|
||||
|
||||
// Theres no way to really test that the icon rendered properly. We just gotta trust the browser to do it's thing :)
|
||||
// However, we can check the <use> size. It should be greater than 0x0 if loaded properly.
|
||||
const rect = use?.getBoundingClientRect();
|
||||
expect(rect?.width).to.be.greaterThan(0);
|
||||
expect(rect?.width).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('Should render nothing if the sprite hash is wrong', async () => {
|
||||
registerIconLibrary('sprite', {
|
||||
resolver: name => `/docs/assets/images/sprite.svg#${name}`,
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor'),
|
||||
spriteSheet: true
|
||||
});
|
||||
|
||||
const el = await fixture<SlIcon>(html`<sl-icon name="non-existant" library="sprite"></sl-icon>`);
|
||||
|
||||
await elementUpdated(el);
|
||||
|
||||
const svg = el.shadowRoot?.querySelector("svg[part='svg']");
|
||||
const use = svg?.querySelector('use');
|
||||
|
||||
// TODO: Theres no way to really test that the icon rendered properly. We just gotta trust the browser to do it's thing :)
|
||||
// However, we can check the <use> size. If it never loaded, it should be 0x0. Ideally, we could have error tracking...
|
||||
const rect = use?.getBoundingClientRect();
|
||||
expect(rect?.width).to.equal(0);
|
||||
expect(rect?.width).to.equal(0);
|
||||
});
|
||||
|
||||
// TODO: <use> svg icons don't emit a "load" or "error" event...if we can figure out how to get the event to emit errors.
|
||||
// Once we figure out how to emit errors / loading perhaps we can actually test this?
|
||||
it.skip("Should produce an error if the icon doesn't exist.", async () => {
|
||||
registerIconLibrary('sprite', {
|
||||
resolver(name) {
|
||||
return `/docs/assets/images/sprite.svg#${name}`;
|
||||
},
|
||||
mutator(svg) {
|
||||
return svg.setAttribute('fill', 'currentColor');
|
||||
},
|
||||
spriteSheet: true
|
||||
});
|
||||
|
||||
const el = await fixture<SlIcon>(html`<sl-icon name="bad-icon" library="sprite"></sl-icon>`);
|
||||
const listener = oneEvent(el, 'sl-error') as Promise<SlErrorEvent>;
|
||||
|
||||
el.name = 'bad-icon';
|
||||
const ev = await listener;
|
||||
await elementUpdated(el);
|
||||
expect(ev).to.exist;
|
||||
});
|
||||
});
|
||||
/* eslint-enable */
|
||||
});
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { getIconLibrary, unwatchIcon, watchIcon } from './library';
|
||||
import { getIconLibrary, type IconLibrary, unwatchIcon, watchIcon } from './library';
|
||||
import { html } from 'lit';
|
||||
import { isTemplateResult } from 'lit/directive-helpers.js';
|
||||
import { watch } from '../../internal/watch';
|
||||
import ShoelaceElement from '../../internal/shoelace-element';
|
||||
import styles from './icon.styles';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
|
||||
import type { CSSResultGroup, HTMLTemplateResult } from 'lit';
|
||||
|
||||
const CACHEABLE_ERROR = Symbol();
|
||||
const RETRYABLE_ERROR = Symbol();
|
||||
type SVGResult = SVGSVGElement | typeof RETRYABLE_ERROR | typeof CACHEABLE_ERROR;
|
||||
type SVGResult = HTMLTemplateResult | SVGSVGElement | typeof RETRYABLE_ERROR | typeof CACHEABLE_ERROR;
|
||||
|
||||
let parser: DOMParser;
|
||||
const iconCache = new Map<string, Promise<SVGResult>>();
|
||||
@@ -18,18 +21,28 @@ const iconCache = new Map<string, Promise<SVGResult>>();
|
||||
* @status stable
|
||||
* @since 2.0
|
||||
*
|
||||
* @event sl-load - Emitted when the icon has loaded.
|
||||
* @event sl-error - Emitted when the icon fails to load due to an error.
|
||||
* @event sl-load - Emitted when the icon has loaded. When using `spriteSheet: true` this will not emit.
|
||||
* @event sl-error - Emitted when the icon fails to load due to an error. When using `spriteSheet: true` this will not emit.
|
||||
*
|
||||
* @csspart svg - The internal SVG element.
|
||||
* @csspart use - The <use> element generated when using `spriteSheet: true`
|
||||
*/
|
||||
@customElement('sl-icon')
|
||||
export default class SlIcon extends ShoelaceElement {
|
||||
static styles: CSSResultGroup = styles;
|
||||
|
||||
private initialRender = false;
|
||||
|
||||
/** Given a URL, this function returns the resulting SVG element or an appropriate error symbol. */
|
||||
private static async resolveIcon(url: string): Promise<SVGResult> {
|
||||
private async resolveIcon(url: string, library?: IconLibrary): Promise<SVGResult> {
|
||||
let fileData: Response;
|
||||
|
||||
if (library?.spriteSheet) {
|
||||
return html`<svg part="svg">
|
||||
<use part="use" href="${url}"></use>
|
||||
</svg>`;
|
||||
}
|
||||
|
||||
try {
|
||||
fileData = await fetch(url, { mode: 'cors' });
|
||||
if (!fileData.ok) return fileData.status === 410 ? CACHEABLE_ERROR : RETRYABLE_ERROR;
|
||||
@@ -57,7 +70,7 @@ export default class SlIcon extends ShoelaceElement {
|
||||
}
|
||||
}
|
||||
|
||||
@state() private svg: SVGElement | null = null;
|
||||
@state() private svg: SVGElement | HTMLTemplateResult | null = null;
|
||||
|
||||
/** The name of the icon to draw. Available names depend on the icon library being used. */
|
||||
@property({ reflect: true }) name?: string;
|
||||
@@ -83,6 +96,7 @@ export default class SlIcon extends ShoelaceElement {
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.initialRender = true;
|
||||
this.setIcon();
|
||||
}
|
||||
|
||||
@@ -126,11 +140,17 @@ export default class SlIcon extends ShoelaceElement {
|
||||
|
||||
let iconResolver = iconCache.get(url);
|
||||
if (!iconResolver) {
|
||||
iconResolver = SlIcon.resolveIcon(url);
|
||||
iconResolver = this.resolveIcon(url, library);
|
||||
iconCache.set(url, iconResolver);
|
||||
}
|
||||
|
||||
// If we haven't rendered yet, exit early. This avoids unnecessary work due to watching multiple props.
|
||||
if (!this.initialRender) {
|
||||
return;
|
||||
}
|
||||
|
||||
const svg = await iconResolver;
|
||||
|
||||
if (svg === RETRYABLE_ERROR) {
|
||||
iconCache.delete(url);
|
||||
}
|
||||
@@ -140,6 +160,11 @@ export default class SlIcon extends ShoelaceElement {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isTemplateResult(svg)) {
|
||||
this.svg = svg;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (svg) {
|
||||
case RETRYABLE_ERROR:
|
||||
case CACHEABLE_ERROR:
|
||||
|
||||
@@ -8,6 +8,7 @@ export interface IconLibrary {
|
||||
name: string;
|
||||
resolver: IconLibraryResolver;
|
||||
mutator?: IconLibraryMutator;
|
||||
spriteSheet?: boolean;
|
||||
}
|
||||
|
||||
let registry: IconLibrary[] = [defaultLibrary, systemLibrary];
|
||||
@@ -29,15 +30,13 @@ export function getIconLibrary(name?: string) {
|
||||
}
|
||||
|
||||
/** Adds an icon library to the registry, or overrides an existing one. */
|
||||
export function registerIconLibrary(
|
||||
name: string,
|
||||
options: { resolver: IconLibraryResolver; mutator?: IconLibraryMutator }
|
||||
) {
|
||||
export function registerIconLibrary(name: string, options: Omit<IconLibrary, 'name'>) {
|
||||
unregisterIconLibrary(name);
|
||||
registry.push({
|
||||
name,
|
||||
resolver: options.resolver,
|
||||
mutator: options.mutator
|
||||
mutator: options.mutator,
|
||||
spriteSheet: options.spriteSheet
|
||||
});
|
||||
|
||||
// Redraw watched icons
|
||||
|
||||
@@ -11,6 +11,7 @@ export default css`
|
||||
}
|
||||
|
||||
.form-control {
|
||||
position: relative;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
@@ -366,6 +366,23 @@ describe('when the value changes', () => {
|
||||
await radioGroup.updateComplete;
|
||||
});
|
||||
|
||||
it('should relatively position content to prevent visually hidden scroll bugs', async () => {
|
||||
//
|
||||
// See https://github.com/shoelace-style/shoelace/issues/1380
|
||||
//
|
||||
const radioGroup = await fixture<SlRadioGroup>(html`
|
||||
<sl-radio-group value="1">
|
||||
<sl-radio id="radio-1" value="1"></sl-radio>
|
||||
</sl-radio-group>
|
||||
`);
|
||||
|
||||
const formControl = radioGroup.shadowRoot!.querySelector('.form-control')!;
|
||||
const visuallyHidden = radioGroup.shadowRoot!.querySelector('.visually-hidden')!;
|
||||
|
||||
expect(getComputedStyle(formControl).position).to.equal('relative');
|
||||
expect(getComputedStyle(visuallyHidden).position).to.equal('absolute');
|
||||
});
|
||||
|
||||
/**
|
||||
* @see https://github.com/shoelace-style/shoelace/issues/1361
|
||||
* This isn't really possible to test right now due to importing "shoelace.js" which
|
||||
|
||||
Reference in New Issue
Block a user