Skip to content
Merged
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
11 changes: 7 additions & 4 deletions pnpm-lock.yaml

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

4 changes: 2 additions & 2 deletions projects/core/.visual/tag.dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions projects/core/.visual/tag.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion projects/core/src/pagination/pagination.test.lighthouse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ describe('pagination lighthouse report', () => {
expect(report.scores.performance).toBe(100);
expect(report.scores.accessibility).toBe(100);
expect(report.scores.bestPractices).toBe(100);
expect(report.payload.javascript.kb).toBeLessThan(38.96);
expect(report.payload.javascript.kb).toBeLessThan(39.5);
});
});
12 changes: 11 additions & 1 deletion projects/core/src/tag/tag.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
}

[internal-host] {
--_color: var(--color);
font-weight: var(--font-weight);
font-size: var(--font-size);
gap: var(--gap);
Expand All @@ -40,7 +41,7 @@
white-space: nowrap;
}

slot {
slot:not([name]) {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
Expand All @@ -51,6 +52,15 @@ slot {
color: inherit !important;
}

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.

The prefix/suffix slots preserve the tags ability to apply text ellipsis overflow while also allowing/fixing the original flex alignments which currently are broken on main.


::slotted(nve-icon) {
--color: var(--_color);
}

::slotted(nve-dot) {
--color: var(--state-color-background);
--status-color: var(--_color);
}

nve-icon {
--color: inherit;
cursor: var(--cursor);
Expand Down
37 changes: 37 additions & 0 deletions projects/core/src/tag/tag.examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export const Closable = {

/**
* @summary Read-only tags for displaying static labels and metadata, providing visual context without user interaction capabilities.
* @tags test-case
*/
export const Readonly = {
render: () => html`
Expand All @@ -75,6 +76,7 @@ export const Group = {

/**
* @summary Comprehensive color palette for tag backgrounds, enabling visual categorization and brand consistency across different content types.
* @tags test-case
*/
export const Color = {
render: () => html`
Expand Down Expand Up @@ -108,6 +110,7 @@ export const Color = {

/**
* @summary High prominence tags with emphasis styling for important categories and priority labels that require visual attention.
* @tags test-case
*/
export const Prominence = {
render: () => html`
Expand Down Expand Up @@ -137,6 +140,40 @@ export const Prominence = {
</div>`
};

/**
* @summary Tag with icon, showing how the icon/dot color inherits the tag color.
* @tags test-case
*/
export const SlottedColor = {
render: () => html`
<div nve-layout="row gap:xs align:wrap">
<nve-tag style="--max-width: 100px"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> very words long here <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag><nve-icon slot="prefix" name="person" size="sm"></nve-icon> default <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="red-cardinal"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> red-cardinal <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="gray-slate"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> gray-slate <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="gray-denim"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> gray-denim <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="blue-indigo"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> blue-indigo <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="blue-cobalt"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> blue-cobalt <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="blue-sky"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> blue-sky <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="teal-cyan"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> teal-cyan <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="green-mint"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> green-mint <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="teal-seafoam"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> teal-seafoam <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="green-grass"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> green-grass <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="yellow-amber"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> yellow-amber <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="orange-pumpkin"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> orange-pumpkin <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="red-tomato"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> red-tomato <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="pink-magenta"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> pink-magenta <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="purple-plum"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> purple-plum <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="purple-violet"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> purple-violet <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="purple-lavender"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> purple-lavender <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="pink-rose"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> pink-rose <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="green-jade"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> green-jade <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="lime-pear"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> lime-pear <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="yellow-nova"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> yellow-nova <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="brand-green"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> brand-green <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
</div>
`
};

/**
* @summary Single tag with constrained width, with text overflow behavior and content truncation in limited space.
Expand Down
15 changes: 15 additions & 0 deletions projects/core/src/tag/tag.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,19 @@ describe(Tag.metadata.tag, () => {
await elementIsStable(element);
expect(element.getAttribute('prominence')).toBe('emphasis');
});

it('should provide a prefix slot', async () => {
await elementIsStable(element);
expect(element.shadowRoot.querySelector('slot[name="prefix"]')).toBeTruthy();
});

it('should provide a suffix slot', async () => {
await elementIsStable(element);
expect(element.shadowRoot.querySelector('slot[name="suffix"]')).toBeTruthy();
});

it('should provide a default slot', async () => {
await elementIsStable(element);
expect(element.shadowRoot.querySelector('slot:not([name])')).toBeTruthy();
});
});
29 changes: 29 additions & 0 deletions projects/core/src/tag/tag.test.visual.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ function template(theme: '' | 'dark' = '') {
return /* html */ `
<script type="module">
import '@nvidia-elements/core/tag/define.js';
import '@nvidia-elements/core/dot/define.js';
import '@nvidia-elements/core/icon/define.js';
document.documentElement.setAttribute('nve-theme', '${theme}');
</script>

Expand Down Expand Up @@ -99,5 +101,32 @@ function template(theme: '' | 'dark' = '') {
<nve-tag prominence="emphasis" color="yellow-nova">•︎•︎•︎•︎•︎•︎</nve-tag>
<nve-tag prominence="emphasis" color="brand-green">•︎•︎•︎•︎•︎•︎</nve-tag>
</div>

<div nve-layout="row gap:xs align:wrap">
<nve-tag style="--max-width: 100px"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ •︎•︎•︎•︎•︎•︎ •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="red-cardinal"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="gray-slate"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="gray-denim"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="blue-indigo"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="blue-cobalt"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="blue-sky"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="teal-cyan"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="green-mint"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="teal-seafoam"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="green-grass"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="yellow-amber"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="orange-pumpkin"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="red-tomato"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="pink-magenta"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="purple-plum"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="purple-violet"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="purple-lavender"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="pink-rose"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="green-jade"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="lime-pear"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="yellow-nova"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
<nve-tag color="brand-green"><nve-icon slot="prefix" name="person" size="sm"></nve-icon> •︎•︎•︎•︎•︎•︎ <nve-dot slot="suffix" size="sm">10</nve-dot></nve-tag>
</div>
`;
}
4 changes: 4 additions & 0 deletions projects/core/src/tag/tag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import styles from './tag.css?inline';
* @since 0.10.0
* @entrypoint \@nvidia-elements/core/tag
* @slot - Text or other content that identifies the category or group.
* @slot prefix - Optional prefix content, such as a status nve-icon.
* @slot suffix - Optional suffix content, such as a nve-dot counter.
* @cssprop --background
* @cssprop --color
* @cssprop --gap
Expand Down Expand Up @@ -71,7 +73,9 @@ export class Tag extends ButtonFormControlMixin(LitElement) {
render() {
return html`
<div internal-host interaction-state focus-within>
<slot name="prefix"></slot>
<slot></slot>
<slot name="suffix"></slot>
${this.closable ? html`<nve-icon part="icon" @click=${() => this.#typeClosableController.close()} container="flat" name="cancel" size="sm" role="img" aria-label=${ifDefined(this.i18n.close)}></nve-icon>` : ''}
</div>
`;
Expand Down
10 changes: 5 additions & 5 deletions projects/internals/metadata/src/services/adoption.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ export class AdoptionService {
packages: [],
github: {
repository: '',
stars: 0,
forks: 0,
subscribers: 0,
contributors: 0,
releases: 0,
stars: null,
forks: null,
subscribers: null,
contributors: null,
releases: null,
stargazers: [],
errors: []
}
Expand Down
7 changes: 6 additions & 1 deletion projects/internals/metadata/src/tasks/adoption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import { resolve } from 'node:path';
import { getAdoptionData } from './adoption.utils.ts';

const adoption = await getAdoptionData();
const errors = [...adoption.packages.flatMap(packageData => packageData.errors), ...adoption.github.errors];

writeFileSync(resolve(import.meta.dirname, '../../static/adoption.json'), JSON.stringify(adoption, null, 2));

console.log('✅ Adoption metrics generated successfully.');
if (errors.length > 0) {
console.warn(`⚠️ Adoption metrics generated with ${errors.length} unavailable source response(s).`);
} else {
console.log('✅ Adoption metrics generated successfully.');
}
25 changes: 25 additions & 0 deletions projects/internals/metadata/src/tasks/adoption.utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,12 @@ describe('adoption utilities', () => {
forks: 4,
subscribers: 3
});
expect(parseGitHubRepository({ stargazers_count: 0, forks_count: 0, subscribers_count: 0 })).toEqual({
stars: 0,
forks: 0,
subscribers: 0
});
expect(parseGitHubRepository({})).toEqual({ stars: null, forks: null, subscribers: null });
expect(
parseGitHubPaginationTotal('<https://api.github.com/repositories/1/releases?per_page=1&page=68>; rel="last"', 1)
).toBe(68);
Expand Down Expand Up @@ -317,6 +323,25 @@ describe('adoption utilities', () => {
expect(metrics.stargazers).toHaveLength(2);
});

it('should distinguish unavailable GitHub metrics from real zero values', async () => {
vi.stubGlobal(
'fetch',
vi.fn(async () => new Response('', { status: 503, statusText: 'Service Unavailable' }))
);

const metrics = await getGitHubMetrics();

expect(metrics).toMatchObject({
stars: null,
forks: null,
subscribers: null,
contributors: null,
releases: null,
stargazers: []
});
expect(metrics.errors).not.toHaveLength(0);
});

it('should create adoption totals without counting unavailable packages as published', () => {
const github = {
repository: 'NVIDIA/elements',
Expand Down
28 changes: 14 additions & 14 deletions projects/internals/metadata/src/tasks/adoption.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ interface NpmRegistryMetadata {
}

interface GitHubRepositoryMetrics {
stars: number;
forks: number;
subscribers: number;
stars: number | null;
forks: number | null;
subscribers: number | null;
}

interface GitHubStargazersResult {
Expand Down Expand Up @@ -301,9 +301,9 @@ export function parseJsDelivrStats(data: unknown, latestVersion: string | null):

export function parseGitHubRepository(data: unknown): GitHubRepositoryMetrics {
return {
stars: getNumber(data, 'stargazers_count') ?? 0,
forks: getNumber(data, 'forks_count') ?? 0,
subscribers: getNumber(data, 'subscribers_count') ?? 0
stars: getNumber(data, 'stargazers_count'),
forks: getNumber(data, 'forks_count'),
subscribers: getNumber(data, 'subscribers_count')
};
}

Expand Down Expand Up @@ -432,22 +432,22 @@ export async function getGitHubMetrics(): Promise<AdoptionGitHubMetrics> {
const errors = [repositoryResult, contributorsResult, releasesResult].flatMap(result =>
result.ok ? [] : [result.error]
);
const repositoryMetrics = repositoryResult.ok
? parseGitHubRepository(repositoryResult.data)
: parseGitHubRepository({});
const repositoryMetrics = repositoryResult.ok ? parseGitHubRepository(repositoryResult.data) : null;
const contributorsFallback =
contributorsResult.ok && Array.isArray(contributorsResult.data) ? contributorsResult.data.length : 0;
const releasesFallback = releasesResult.ok && Array.isArray(releasesResult.data) ? releasesResult.data.length : 0;

return {
repository,
stars: repositoryMetrics.stars,
forks: repositoryMetrics.forks,
subscribers: repositoryMetrics.subscribers,
stars: repositoryMetrics?.stars ?? null,
forks: repositoryMetrics?.forks ?? null,
subscribers: repositoryMetrics?.subscribers ?? null,
contributors: contributorsResult.ok
? parseGitHubPaginationTotal(contributorsResult.headers.get('link'), contributorsFallback)
: 0,
releases: releasesResult.ok ? parseGitHubPaginationTotal(releasesResult.headers.get('link'), releasesFallback) : 0,
: null,
releases: releasesResult.ok
? parseGitHubPaginationTotal(releasesResult.headers.get('link'), releasesFallback)
: null,
stargazers: parseGitHubStargazers(stargazersResult.data),
errors: [...errors, ...stargazersResult.errors]
};
Expand Down
Loading
Loading