diff --git a/.changeset/sharp-bulldogs-run.md b/.changeset/sharp-bulldogs-run.md
new file mode 100644
index 0000000000..eb7aa31724
--- /dev/null
+++ b/.changeset/sharp-bulldogs-run.md
@@ -0,0 +1,5 @@
+---
+"@digdir/designsystemet-web": patch
+---
+
+**All components:** Renders instantly for easier test setup
diff --git a/apps/themebuilder/app/_components/color-preview/color-preview.tsx b/apps/themebuilder/app/_components/color-preview/color-preview.tsx
index 3786c4a09d..6c16b9a2ca 100644
--- a/apps/themebuilder/app/_components/color-preview/color-preview.tsx
+++ b/apps/themebuilder/app/_components/color-preview/color-preview.tsx
@@ -56,7 +56,7 @@ export const ColorPreview = () => {
{t('colorPreview.view')}
setView(value as ViewType)}
diff --git a/apps/themebuilder/app/_components/previews/previews.tsx b/apps/themebuilder/app/_components/previews/previews.tsx
index 133d7f67fb..9e2a672569 100644
--- a/apps/themebuilder/app/_components/previews/previews.tsx
+++ b/apps/themebuilder/app/_components/previews/previews.tsx
@@ -36,7 +36,7 @@ export const Previews = () => {
<>
setTheme(v as keyof typeof themes)}
>
@@ -48,7 +48,7 @@ export const Previews = () => {
setColorScheme(v as ColorScheme)}
>
diff --git a/apps/www/app/_components/live-component/live-components.tsx b/apps/www/app/_components/live-component/live-components.tsx
index 5167abc5ea..6300550f56 100644
--- a/apps/www/app/_components/live-component/live-components.tsx
+++ b/apps/www/app/_components/live-component/live-components.tsx
@@ -184,7 +184,7 @@ const Editor = ({ live, html, id, hidden, language }: EditorProps) => {
setShowHTML(v === 'true')}
diff --git a/apps/www/app/content/blog/en/designsystemet-web-pre2.mdx b/apps/www/app/content/blog/en/designsystemet-web-pre2.mdx
index d460b128b0..007c466d4f 100644
--- a/apps/www/app/content/blog/en/designsystemet-web-pre2.mdx
+++ b/apps/www/app/content/blog/en/designsystemet-web-pre2.mdx
@@ -35,7 +35,7 @@ Please check all tests and console to look for errors or unexpected behavior.
`ToggleGroup` still uses `ToggleGroup.Item` in React, but now internally does not create `
`;
- await waitForField();
};
describe('Field component', () => {
- it('should add id and connect label and input', async () => {
- await renderDefault();
+ it('should add id and connect label and input', () => {
+ render();
const label = document.querySelector('label');
const input = document.querySelector('input');
@@ -33,8 +28,8 @@ describe('Field component', () => {
);
});
- it('should set aria-invalid when validation message is present', async () => {
- await renderDefault();
+ it('should set aria-invalid when validation message is present', () => {
+ render();
const input = document.querySelector('input');
@@ -42,13 +37,12 @@ describe('Field component', () => {
expect(input).toHaveAttribute('aria-invalid', 'true');
});
- test('should update counter live region', async () => {
+ test('should update counter live region', () => {
document.body.innerHTML = `
`;
- await waitForField();
const textarea = document.querySelector('textarea');
const counter = document.querySelector('[data-field="counter"]');
@@ -67,7 +61,6 @@ describe('Field component', () => {
`;
- await waitForField();
const textarea = document.querySelector('textarea');
const counter = document.querySelector('[data-field="counter"]');
@@ -76,12 +69,8 @@ describe('Field component', () => {
expect(counter?.getAttribute('data-label')).toBe('13 tegn for mye');
counter?.setAttribute('data-limit', '10');
+ await new Promise((resolve) => setTimeout(resolve, 0)); // Let MutationObserver in JS Event Loop run
- expect(
- vi.waitUntil(
- () => counter?.getAttribute('data-label') === '23 tegn for mye',
- 2000,
- ),
- ).toBeTruthy();
+ expect(counter?.getAttribute('data-label')).toBe('23 tegn for mye');
});
});
diff --git a/packages/web/src/field/field.ts b/packages/web/src/field/field.ts
index 252f642421..3a12aeb530 100644
--- a/packages/web/src/field/field.ts
+++ b/packages/web/src/field/field.ts
@@ -5,7 +5,6 @@ import {
customElements,
DSElement,
debounce,
- isBrowser,
isWindows,
on,
onHotReload,
@@ -22,78 +21,94 @@ declare global {
}
}
-const INDETERMINATE = 'data-indeterminate';
-const FIELDS = new Set(); // Set of Field
-const COUNTS = new WeakMap(); // Using WeakMap so removed inputs/counts does not cause memory leaks
-const FIELDSETS = isBrowser() ? document.getElementsByTagName('fieldset') : [];
-const HAS_FIELD_SIZING = isBrowser() && CSS.supports('field-sizing', 'content');
+const ATTR_DESCRIBEDBY = 'aria-describedby';
+const ATTR_INDETERMINATE = 'data-indeterminate';
const COUNTER_DEBOUNCE = isWindows() ? 800 : 200; // Longer debounce on Windows due to NVDA performance
-const HAS_VALIDATION = new WeakSet(); // Used to store inputs that have/had validation elements to manage aria-invalid
-
-const handleMutations = debounce(() => {
- for (const el of FIELDSETS) {
- const labelledby = `${useId(el.querySelector('legend'))} ${useId(el.querySelector(':scope > :is([data-field="description"],legend + p)'))}`;
- attr(el, 'aria-labelledby', labelledby.trim() || null);
+const COUNTS = new WeakMap(); // Using WeakMap so removed inputs/counts does not cause memory leaks
+const FIELDS = new Map(); // Map of Field and its describedby IDs so we can identify the ones we add/remove
+const VALIDATIONS = new WeakMap(); // Used to ensure we only take control of aria-invalid if there current is or has been a validation element
+const WARNING_MULTIPLE_INPUTS = `Fields should only have one input element. Use