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
3 changes: 3 additions & 0 deletions projects/core/.visual/progress-dot.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/core/.visual/progress-dot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions projects/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,18 @@
"types": "./dist/progress-bar/define.d.ts",
"default": "./dist/progress-bar/define.js"
},
"./progress-dot": {
"types": "./dist/progress-dot/index.d.ts",
"default": "./dist/progress-dot/index.js"
},
"./progress-dot/index.js": {
"types": "./dist/progress-dot/index.d.ts",
"default": "./dist/progress-dot/index.js"
},
"./progress-dot/define.js": {
"types": "./dist/progress-dot/define.d.ts",
"default": "./dist/progress-dot/define.js"
},
"./progress-ring": {
"types": "./dist/progress-ring/index.d.ts",
"default": "./dist/progress-ring/index.js"
Expand Down
2 changes: 2 additions & 0 deletions projects/core/src/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import '@nvidia-elements/core/panel/define.js';
import '@nvidia-elements/core/password/define.js';
import '@nvidia-elements/core/preferences-input/define.js';
import '@nvidia-elements/core/progress-bar/define.js';
import '@nvidia-elements/core/progress-dot/define.js';
import '@nvidia-elements/core/progress-ring/define.js';
import '@nvidia-elements/core/progressive-filter-chip/define.js';
import '@nvidia-elements/core/pulse/define.js';
Expand Down Expand Up @@ -123,6 +124,7 @@ export * from '@nvidia-elements/core/panel';
export * from '@nvidia-elements/core/password';
export * from '@nvidia-elements/core/preferences-input';
export * from '@nvidia-elements/core/progress-bar';
export * from '@nvidia-elements/core/progress-dot';
export * from '@nvidia-elements/core/progress-ring';
export * from '@nvidia-elements/core/progressive-filter-chip';
export * from '@nvidia-elements/core/pulse';
Expand Down
1 change: 1 addition & 0 deletions projects/core/src/index.test.lighthouse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ describe('lighthouse report', () => {
import '@nvidia-elements/core/password/define.js';
import '@nvidia-elements/core/preferences-input/define.js';
import '@nvidia-elements/core/progress-bar/define.js';
import '@nvidia-elements/core/progress-dot/define.js';
import '@nvidia-elements/core/progress-ring/define.js';
import '@nvidia-elements/core/progressive-filter-chip/define.js';
import '@nvidia-elements/core/pulse/define.js';
Expand Down
13 changes: 13 additions & 0 deletions projects/core/src/progress-dot/define.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { define } from '@nvidia-elements/core/internal';
import { ProgressDot } from '@nvidia-elements/core/progress-dot';

define(ProgressDot);

declare global {
interface HTMLElementTagNameMap {
'nve-progress-dot': ProgressDot;
}
}
4 changes: 4 additions & 0 deletions projects/core/src/progress-dot/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

export * from './progress-dot.js';
72 changes: 72 additions & 0 deletions projects/core/src/progress-dot/progress-dot.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
/* SPDX-License-Identifier: Apache-2.0 */

:host {
--color: currentColor;
--width: var(--nve-ref-size-500);
--height: var(--nve-ref-size-500);
--animation-duration: calc(var(--nve-ref-animation-duration-400) * 1.5);
display: inline-flex;
color: inherit;
}

:host([size='sm']) {
--width: var(--nve-ref-size-400);
--height: var(--nve-ref-size-400);
}

:host([size='lg']) {
--width: var(--nve-ref-size-600);
--height: var(--nve-ref-size-600);
}

[internal-host] {
width: var(--width);
height: var(--height);
display: flex;
align-items: center;
justify-content: center;
}

svg {
width: 100%;
height: 100%;
fill: var(--color);
}

circle {
opacity: 1;
animation: fade var(--animation-duration) ease-in-out infinite;
}

circle:nth-child(2) {
animation-delay: calc(var(--animation-duration) / 3);
}

circle:nth-child(3) {
animation-delay: calc(var(--animation-duration) * 2 / 3);
}

@keyframes fade {
0%,
100% {
opacity: 1;
}

50% {
opacity: var(--nve-ref-opacity-200);
}
}

@media (prefers-reduced-motion: reduce) {
circle {
animation: none;
}
}

/* A zero short-duration token means the active theme suppresses motion. */
@container style(--nve-ref-animation-duration-150: 0) {
circle {
animation: none;
}
}
83 changes: 83 additions & 0 deletions projects/core/src/progress-dot/progress-dot.examples.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { html } from 'lit';
import '@nvidia-elements/core/progress-dot/define.js';

export default {
title: 'Elements/Progress Dot',
component: 'nve-progress-dot'
};

/**
* @summary Indeterminate dots communicate ongoing work when a completion percentage is unavailable.
*/
export const Default = {
render: () => html`<nve-progress-dot aria-label="Loading results"></nve-progress-dot>`
};

/**
* @summary Three indicator sizes fit loading feedback into compact rows or spacious panels.
*/
export const Size = {
render: () => html`
<div nve-layout="row gap:sm align:vertical-center">
<nve-progress-dot size="sm" aria-label="Loading results"></nve-progress-dot>
<nve-progress-dot size="md" aria-label="Loading results"></nve-progress-dot>
<nve-progress-dot size="lg" aria-label="Loading results"></nve-progress-dot>
</div>
`
};

/**
* @summary An adjacent label describes the pending task and supplies the indicator's accessible name.
*/
export const WithText = {
render: () => html`
<div nve-layout="row gap:xs align:vertical-center">
<nve-progress-dot size="sm" aria-labelledby="progress-dot-label"></nve-progress-dot>
<span id="progress-dot-label">Generating response…</span>
</div>
`
};

/**
* @summary Button indicators inherit text color to keep loading feedback visible across button treatments.
*/
export const WithButton = {
render: () => html`
<div nve-layout="row gap:sm">
<nve-button disabled aria-busy="true">
<nve-progress-dot size="sm" aria-hidden="true"></nve-progress-dot>
Saving…
</nve-button>
<nve-button interaction="emphasis" disabled aria-busy="true">
<nve-progress-dot size="sm" aria-hidden="true"></nve-progress-dot>
Saving…
</nve-button>
</div>
`
};

/**
* @summary Custom color, dimensions, and timing adapt loading feedback to its surrounding interface.
* @tags theme
*/
export const CustomStyles = {
render: () => html`
<nve-progress-dot
aria-label="Loading results"
style="--color: var(--nve-sys-support-accent-color); --width: var(--nve-ref-size-1000); --height: var(--nve-ref-size-600); --animation-duration: 2s"
></nve-progress-dot>
`
};

/**
* @summary Static dots keep loading feedback visible when animation is unnecessary or distracting.
* @tags test-case
*/
export const Static = {
render: () => html`
<nve-progress-dot aria-label="Loading results" style="--animation-duration: 0s"></nve-progress-dot>
`
};
32 changes: 32 additions & 0 deletions projects/core/src/progress-dot/progress-dot.test.axe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { html } from 'lit';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { createFixture, elementIsStable, removeFixture } from '@internals/testing';
import { runAxe } from '@internals/testing/axe';
import { ProgressDot } from '@nvidia-elements/core/progress-dot';
import '@nvidia-elements/core/progress-dot/define.js';

describe(ProgressDot.metadata.tag, () => {
let fixture: HTMLElement;

beforeEach(async () => {
fixture = await createFixture(html`
<nve-progress-dot size="sm"></nve-progress-dot>
<nve-progress-dot size="md" aria-label="Loading results"></nve-progress-dot>
<span id="progress-dot-label">Generating response</span>
<nve-progress-dot size="lg" aria-labelledby="progress-dot-label"></nve-progress-dot>
<nve-progress-dot aria-label="Loading results" style="--animation-duration: 0s"></nve-progress-dot>
<nve-progress-dot aria-hidden="true"></nve-progress-dot>
`);
await Promise.all([...fixture.querySelectorAll('nve-progress-dot')].map(element => elementIsStable(element)));
});

afterEach(() => removeFixture(fixture));

it('should pass axe for all sizes and label sources', async () => {
const results = await runAxe([ProgressDot.metadata.tag]);
expect(results.violations.length).toBe(0);
});
});
21 changes: 21 additions & 0 deletions projects/core/src/progress-dot/progress-dot.test.lighthouse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { describe, expect, test } from 'vitest';
import { lighthouseRunner } from '@internals/vite';

describe('progress-dot lighthouse report', () => {
test('should meet lighthouse benchmarks', async () => {
const report = await lighthouseRunner.getReport('nve-progress-dot', /* html */ `
<nve-progress-dot aria-label="Loading results"></nve-progress-dot>
<script type="module">
import '@nvidia-elements/core/progress-dot/define.js';
</script>
`);

expect(report.scores.performance).toBe(100);
expect(report.scores.accessibility).toBe(100);
expect(report.scores.bestPractices).toBe(100);
expect(report.payload.javascript.kb).toBeLessThan(12);
});
});
23 changes: 23 additions & 0 deletions projects/core/src/progress-dot/progress-dot.test.ssr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { html } from 'lit';
import { describe, expect, it } from 'vitest';
import { ssrRunner } from '@internals/vite';
import { ProgressDot } from '@nvidia-elements/core/progress-dot';
import '@nvidia-elements/core/progress-dot/define.js';

describe(ProgressDot.metadata.tag, () => {
it('should render three decorative dots and preserve author configuration', async () => {
const result = await ssrRunner.render(html`
<nve-progress-dot size="sm" aria-label="Loading results"></nve-progress-dot>
`);
expect(result).toContain('shadowroot="open"');
expect(result).toContain('nve-progress-dot');
expect(result).toContain('size="sm"');
expect(result).toContain('aria-label="Loading results"');
expect(result).toContain('aria-hidden="true"');
expect(result.match(/<circle /g)).toHaveLength(3);
expect(result).not.toContain('aria-valuenow');
});
});
Loading
Loading