Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions knip.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ export default {
entry: [
...PACKAGE_FILES,
...SOURCE_INDEX,
...EXAMPLE_ENTRIES,
...TEST_ENTRIES,
...TEST_VARIANT_ENTRIES,
...VITE_CONFIGS,
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 36 additions & 1 deletion projects/internals/metadata/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@
"../../forms/dist/custom-elements.json",
"../../markdown/package.json",
"../../markdown/dist/custom-elements.json",
"../../media/package.json",
"../../media/dist/custom-elements.json",
"../../monaco/package.json",
"../../monaco/dist/custom-elements.json",
"../../patterns/package.json",
Expand Down Expand Up @@ -142,6 +144,10 @@
"script": "../../markdown:build",
"cascade": false
},
{
"script": "../../media:build",
"cascade": false
},
{
"script": "../../monaco:build",
"cascade": false
Expand Down Expand Up @@ -171,6 +177,9 @@
"../../markdown/package.json",
"../../markdown/README.md",
"../../markdown/CHANGELOG.md",
"../../media/package.json",
"../../media/README.md",
"../../media/CHANGELOG.md",
"../../monaco/package.json",
"../../monaco/README.md",
"../../monaco/CHANGELOG.md",
Expand Down Expand Up @@ -212,6 +221,11 @@
"../../markdown/coverage/unit/summary.json",
"../../markdown/coverage/visual/summary.json",
"../../markdown/coverage/ssr/summary.json",
"../../media/coverage/unit/coverage-summary.json",
"../../media/coverage/unit/summary.json",
"../../media/coverage/axe/summary.json",
"../../media/coverage/visual/summary.json",
"../../media/coverage/ssr/summary.json",
"../../monaco/coverage/unit/coverage-summary.json",
"../../monaco/coverage/unit/summary.json",
"../../monaco/coverage/visual/summary.json",
Expand Down Expand Up @@ -259,6 +273,22 @@
"script": "../../markdown:test:coverage",
"cascade": false
},
{
"script": "../../media:test:coverage",
"cascade": false
},
{
"script": "../../media:test:axe",
"cascade": false
},
{
"script": "../../media:test:ssr",
"cascade": false
},
{
"script": "../../media:test:visual",
"cascade": false
},
{
"script": "../../monaco:test:coverage",
"cascade": false
Expand Down Expand Up @@ -291,7 +321,8 @@
"../../monaco/dist/**/*.examples.json",
"../../code/dist/**/*.examples.json",
"../../forms/dist/**/*.examples.json",
"../../markdown/dist/**/*.examples.json"
"../../markdown/dist/**/*.examples.json",
"../../media/dist/**/*.examples.json"
],
"output": [
"static/examples.json"
Expand Down Expand Up @@ -321,6 +352,10 @@
"script": "../../monaco:build",
"cascade": false
},
{
"script": "../../media:build",
"cascade": false
},
{
"script": "../patterns:build",
"cascade": false
Expand Down
8 changes: 7 additions & 1 deletion projects/internals/metadata/src/services/api.service.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { describe, expect, it } from 'vitest';
import MiniSearch from 'minisearch';
import { describe, expect, it, vi } from 'vitest';
import { ApiService } from './api.service.js';

describe('ApiService', () => {
Expand Down Expand Up @@ -41,7 +42,12 @@ describe('ApiService', () => {
});

it('should prioritize exact matches over fuzzy matches', async () => {
const searchSpy = vi.spyOn(MiniSearch.prototype, 'search').mockReturnValue([
{ id: 'nve-button-group', terms: ['button'], queryTerms: ['button'], score: 2, match: {} },
{ id: 'nve-button', terms: ['button'], queryTerms: ['button'], score: 1, match: {} }
]);
const results = await ApiService.search('nve-button');
searchSpy.mockRestore();
Comment on lines +45 to +50

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C 3 'restoreMocks|mockRestore|spyOn\(MiniSearch\.prototype' \
  projects/internals/metadata

Repository: NVIDIA/elements

Length of output: 2118


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the test file context and repo-level Vitest config/cleanup.
sed -n '1,140p' projects/internals/metadata/src/services/api.service.test.ts

printf '\n--- vitest config ---\n'
rg -n -C 2 'restoreMocks|clearMocks|mockReset|afterEach\\(|beforeEach\\(' \
  projects/internals/metadata \
  -g 'vitest*.{ts,js,mjs,cjs}' \
  -g 'vite*.{ts,js,mjs,cjs}' \
  -g 'test*.{ts,js,mjs,cjs}'

printf '\n--- package configs ---\n'
rg -n -C 2 '"test"|vitest|restoreMocks|clearMocks|mockReset' \
  projects/internals/metadata/package.json \
  projects/internals/metadata/**/package.json \
  projects/internals/metadata/**/vitest*.{ts,js,mjs,cjs} \
  projects/internals/metadata/**/vite*.{ts,js,mjs,cjs}

Repository: NVIDIA/elements

Length of output: 2541


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- api.service.test.ts ---\n'
sed -n '1,120p' projects/internals/metadata/src/services/api.service.test.ts

printf '\n--- restoreMocks / cleanup references ---\n'
rg -n -C 2 'restoreMocks|clearMocks|mockReset|mockRestore|afterEach\\(|beforeEach\\(' projects/internals/metadata

printf '\n--- vitest/vite config files ---\n'
fd -a 'vitest*.{ts,js,mjs,cjs}' projects/internals/metadata
fd -a 'vite*.{ts,js,mjs,cjs}' projects/internals/metadata
fd -a 'test*.{ts,js,mjs,cjs}' projects/internals/metadata

Repository: NVIDIA/elements

Length of output: 244


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 2 'restoreMocks|clearMocks|mockReset|mockRestore|afterEach\\(|beforeEach\\(' \
  . \
  -g 'vitest*.{ts,js,mjs,cjs}' \
  -g 'vite*.{ts,js,mjs,cjs}' \
  -g 'test*.{ts,js,mjs,cjs}' \
  -g 'package.json'

printf '\n--- candidate config files ---\n'
fd -a 'vitest*.{ts,js,mjs,cjs}' .
fd -a 'vite*.{ts,js,mjs,cjs}' .
fd -a 'test*.{ts,js,mjs,cjs}' .

Repository: NVIDIA/elements

Length of output: 345


🏁 Script executed:

#!/bin/bash
set -euo pipefail

wc -l projects/internals/metadata/src/services/api.service.test.ts
sed -n '1,220p' projects/internals/metadata/src/services/api.service.test.ts

Repository: NVIDIA/elements

Length of output: 2414


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Repo-wide Vitest/Vite/package config references without regex grouping.
rg -n -C 2 -e 'restoreMocks' -e 'clearMocks' -e 'mockReset' -e 'mockRestore' -e 'afterEach(' -e 'beforeEach(' \
  . \
  -g 'vitest*.ts' -g 'vitest*.js' -g 'vitest*.mjs' -g 'vitest*.cjs' \
  -g 'vite*.ts' -g 'vite*.js' -g 'vite*.mjs' -g 'vite*.cjs' \
  -g 'package.json'

printf '\n--- package files ---\n'
fd -a 'package.json' projects/internals/metadata
fd -a 'vitest*.ts' projects/internals/metadata
fd -a 'vite*.ts' projects/internals/metadata

printf '\n--- api.service implementation ---\n'
sed -n '1,220p' projects/internals/metadata/src/services/api.service.ts

Repository: NVIDIA/elements

Length of output: 378


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,220p' projects/internals/metadata/src/services/api.service.ts

Repository: NVIDIA/elements

Length of output: 1770


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 2 -F 'restoreMocks' . -g 'package.json' -g 'vitest*.ts' -g 'vitest*.js' -g 'vitest*.mjs' -g 'vitest*.cjs' -g 'vite*.ts' -g 'vite*.js' -g 'vite*.mjs' -g 'vite*.cjs'
rg -n -C 2 -F 'clearMocks' . -g 'package.json' -g 'vitest*.ts' -g 'vitest*.js' -g 'vitest*.mjs' -g 'vitest*.cjs' -g 'vite*.ts' -g 'vite*.js' -g 'vite*.mjs' -g 'vite*.cjs'
rg -n -C 2 -F 'mockReset' . -g 'package.json' -g 'vitest*.ts' -g 'vitest*.js' -g 'vitest*.mjs' -g 'vitest*.cjs' -g 'vite*.ts' -g 'vite*.js' -g 'vite*.mjs' -g 'vite*.cjs'
rg -n -C 2 -F 'mockRestore' projects/internals/metadata/src

Repository: NVIDIA/elements

Length of output: 153


Restore the prototype spy in finally. Wrap the ApiService.search('nve-button') call so searchSpy.mockRestore() still runs if the test throws.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@projects/internals/metadata/src/services/api.service.test.ts` around lines 45
- 50, Wrap the ApiService.search call in a try/finally block so the
MiniSearch.prototype spy created by searchSpy is always restored, including when
the search throws. Keep the existing search arguments and result assertions
unchanged, and move searchSpy.mockRestore() into the finally block.


expect(results[0]?.name).toBe('nve-button');
});
Expand Down
57 changes: 57 additions & 0 deletions projects/internals/metadata/src/tasks/api.utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,65 @@
import { describe, expect, it } from 'vitest';
import { getApi } from './api.utils.js';

type Api = Awaited<ReturnType<typeof getApi>>;
type ApiElement = Api['data']['elements'][number];

function getElement(api: Api, name: string) {
return api.data.elements.find(element => element.name === name);
}

function getMember(element: ApiElement | undefined, name: string) {
return element?.manifest?.members.find(member => member.name === name);
}

function getAttribute(element: ApiElement | undefined, name: string) {
return element?.manifest?.attributes?.find(attribute => attribute.name === name);
}

describe('ApiUtils', () => {
it('should return the api json', async () => {
expect(getApi).toBeDefined();
});

it('should include attribute names for inherited button members', async () => {
const api = await getApi();
const button = getElement(api, 'nve-button');

expect(getMember(button, 'pressed')?.attribute).toBe('pressed');
expect(getMember(button, 'readOnly')?.attribute).toBe('readonly');
expect(getMember(button, 'commandForElement')?.attribute).toBe('commandfor');
expect(button?.markdown).toContain('| readOnly (readonly) |');
});

it('should project checkbox and button mixin APIs onto media mute button', async () => {
const api = await getApi();
const muteButton = getElement(api, 'nve-media-mute-button');

expect(getMember(muteButton, 'pressed')?.attribute).toBe('pressed');
expect(getMember(muteButton, 'checked')?.attribute).toBe('checked');
expect(getMember(muteButton, 'readOnly')?.attribute).toBe('readonly');
expect(getMember(muteButton, 'commandForElement')?.attribute).toBe('commandfor');
expect(getAttribute(muteButton, 'commandForElement')).toBeUndefined();
expect(muteButton?.markdown).toContain('| checked |');
});

it('should project slider mixin APIs onto media time range', async () => {
const api = await getApi();
const timeRange = getElement(api, 'nve-media-time-range');

expect(getMember(timeRange, 'min')?.attribute).toBe('min');
expect(getMember(timeRange, 'valueAsNumber')?.attribute).toBeUndefined();
expect(getAttribute(timeRange, 'commandForElement')).toBeUndefined();
expect(timeRange?.markdown).toContain('| valueAsNumber |');
});

it('should project select mixin APIs onto media playback rate select', async () => {
const api = await getApi();
const playbackRateSelect = getElement(api, 'nve-media-playback-rate-select');

expect(getMember(playbackRateSelect, 'selectedIndex')?.attribute).toBeUndefined();
expect(getMember(playbackRateSelect, 'value')?.attribute).toBe('value');
expect(getAttribute(playbackRateSelect, 'commandForElement')).toBeUndefined();
expect(playbackRateSelect?.markdown).toContain('| selectedIndex |');
});
});
1 change: 1 addition & 0 deletions projects/internals/metadata/src/tasks/api.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export async function getApi(): Promise<{
'../../../../themes',
'../../../../code',
'../../../../markdown',
'../../../../media',
'../../../../monaco'
];

Expand Down
4 changes: 2 additions & 2 deletions projects/internals/metadata/static/adoption.json
Git LFS file not shown
4 changes: 2 additions & 2 deletions projects/internals/metadata/static/releases.json
Git LFS file not shown
4 changes: 2 additions & 2 deletions projects/internals/metadata/static/tests.json
Git LFS file not shown
30 changes: 30 additions & 0 deletions projects/internals/tools/src/api/service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,43 @@ describe('ApiService', () => {
const result = await ApiService.get({ names: 'nve-button', format: 'markdown' });
expect(typeof result).toBe('string');
expect(result as string).toContain('nve-button');
expect(result as string).toContain('| readOnly (readonly) |');
});

it('should return projected mixin api for directly mixed components', async () => {
const result = await ApiService.get({ names: 'nve-media-mute-button', format: 'markdown' });
expect(typeof result).toBe('string');
expect(result as string).toContain('| pressed |');
expect(result as string).toContain('| checked |');
expect(result as string).toContain('| readOnly (readonly) |');
expect(result as string).toContain('| commandForElement (commandfor) |');
expect(result as string).not.toContain('| commandForElement (commandForElement) |');
});

it('should return json for a single string name', async () => {
const result = (await ApiService.get({ names: 'nve-button', format: 'json' })) as (Element | Attribute)[];
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(1);
expect(result[0].name).toBe('nve-button');

const button = result[0] as Element;
const readOnly = button.manifest?.members.find(member => member.name === 'readOnly');

expect(readOnly?.attribute).toBe('readonly');
});

it('should return json with projected mixin attributes for directly mixed components', async () => {
const result = (await ApiService.get({ names: 'nve-media-mute-button', format: 'json' })) as (
| Element
| Attribute
)[];
const muteButton = result[0] as Element;
const checked = muteButton.manifest?.members.find(member => member.name === 'checked');
const commandForElement = muteButton.manifest?.members.find(member => member.name === 'commandForElement');

expect(checked?.attribute).toBe('checked');
expect(commandForElement?.attribute).toBe('commandfor');
expect(muteButton.manifest?.attributes?.some(attribute => attribute.name === 'commandForElement')).toBe(false);
});

it('should return markdown for an array with one name', async () => {
Expand Down
2 changes: 2 additions & 0 deletions projects/internals/tools/src/playground/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,8 @@ function createImportMap(framework: 'react' | 'preact' | 'angular' | 'lit' | 'vu
'@nvidia-elements/code/': `${CDN_MODULES_URL}/@nvidia-elements/code@latest/`,
'@nvidia-elements/forms': `${CDN_MODULES_URL}/@nvidia-elements/forms@latest`,
'@nvidia-elements/forms/': `${CDN_MODULES_URL}/@nvidia-elements/forms@latest/`,
'@nvidia-elements/media': `${CDN_MODULES_URL}/@nvidia-elements/media@latest`,
'@nvidia-elements/media/': `${CDN_MODULES_URL}/@nvidia-elements/media@latest/`,
...(frameworkImportMap[framework]?.(CDN_MODULES_URL) ?? {})
};

Expand Down
14 changes: 11 additions & 3 deletions projects/lint/src/eslint/rules/no-invalid-invoker-triggers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import noInvalidInvokerTriggers from './no-invalid-invoker-triggers.js';
const rule = noInvalidInvokerTriggers as unknown as JSRuleDefinition;

const validElements =
'nve-button, nve-icon-button, nve-menu-item, nve-sort-button, nve-tabs-item, nve-tag, nve-steps-item, nve-copy-button';
'nve-button, nve-icon-button, nve-menu-item, nve-sort-button, nve-tabs-item, nve-tag, nve-steps-item, nve-copy-button, nve-media-fullscreen-button, nve-media-mute-button, nve-media-pause-button, nve-media-playback-rate-select, nve-media-seek-button, nve-media-time-range, nve-media-volume-range';

describe('noInvalidInvokerTriggers', () => {
let tester: RuleTester;
Expand Down Expand Up @@ -50,7 +50,8 @@ describe('noInvalidInvokerTriggers', () => {
'<nve-tabs-item popovertarget="my-popover">Tab</nve-tabs-item>',
'<nve-tag popovertarget="my-popover">Tag</nve-tag>',
'<nve-steps-item popovertarget="my-popover">Step</nve-steps-item>',
'<nve-copy-button popovertarget="my-popover"></nve-copy-button>'
'<nve-copy-button popovertarget="my-popover"></nve-copy-button>',
'<nve-media-fullscreen-button popovertarget="my-popover"></nve-media-fullscreen-button>'
],
invalid: []
});
Expand All @@ -61,7 +62,14 @@ describe('noInvalidInvokerTriggers', () => {
valid: [
'<nve-button commandfor="my-panel">Toggle</nve-button>',
'<nve-icon-button commandfor="my-panel"></nve-icon-button>',
'<nve-tag commandfor="my-panel">Tag</nve-tag>'
'<nve-tag commandfor="my-panel">Tag</nve-tag>',
'<nve-media-pause-button commandfor="my-player"></nve-media-pause-button>',
'<nve-media-playback-rate-select commandfor="my-player"></nve-media-playback-rate-select>',
'<nve-media-mute-button commandfor="my-player"></nve-media-mute-button>',
'<nve-media-seek-button commandfor="my-player"></nve-media-seek-button>',
'<nve-media-time-range commandfor="my-player"></nve-media-time-range>',
'<nve-media-volume-range commandfor="my-player"></nve-media-volume-range>',
'<nve-media-fullscreen-button commandfor="my-player"></nve-media-fullscreen-button>'
],
invalid: []
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,14 @@ const BUTTON_TYPE_ELEMENTS = [
'nve-tabs-item',
'nve-tag',
'nve-steps-item',
'nve-copy-button'
'nve-copy-button',
'nve-media-fullscreen-button',
'nve-media-mute-button',
'nve-media-pause-button',
'nve-media-playback-rate-select',
'nve-media-seek-button',
'nve-media-time-range',
'nve-media-volume-range'
Comment thread
coryrylan marked this conversation as resolved.
] as const;

const rule = {
Expand Down
3 changes: 3 additions & 0 deletions projects/media/.visual/media-controller.dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions projects/media/.visual/media-controller.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions projects/media/.visual/media-fullscreen-button.dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions projects/media/.visual/media-fullscreen-button.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions projects/media/.visual/media-mute-button.dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions projects/media/.visual/media-mute-button.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions projects/media/.visual/media-pause-button.dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions projects/media/.visual/media-pause-button.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions projects/media/.visual/media-playback-rate-select.dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions projects/media/.visual/media-playback-rate-select.png

@coryrylan coryrylan Jul 22, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: select should have inline style disable like icon button here

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions projects/media/.visual/media-seek-button.dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions projects/media/.visual/media-seek-button.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions projects/media/.visual/media-time-range.dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions projects/media/.visual/media-time-range.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions projects/media/.visual/media-volume-range.dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions projects/media/.visual/media-volume-range.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading