diff --git a/angular-workspace/projects/ni/nimble-angular/CHANGELOG.json b/angular-workspace/projects/ni/nimble-angular/CHANGELOG.json index 9e9b860a35..f170144ec8 100644 --- a/angular-workspace/projects/ni/nimble-angular/CHANGELOG.json +++ b/angular-workspace/projects/ni/nimble-angular/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@ni/nimble-angular", "entries": [ + { + "date": "Wed, 20 Mar 2024 16:45:59 GMT", + "version": "20.5.1", + "tag": "@ni/nimble-angular_v20.5.1", + "comments": { + "patch": [ + { + "author": "beachball", + "package": "@ni/nimble-angular", + "comment": "Bump @ni/nimble-components to v22.1.1", + "commit": "not available" + } + ] + } + }, { "date": "Mon, 18 Mar 2024 17:12:34 GMT", "version": "20.5.0", diff --git a/angular-workspace/projects/ni/nimble-angular/CHANGELOG.md b/angular-workspace/projects/ni/nimble-angular/CHANGELOG.md index f7aafd80de..1a2aa3c1ca 100644 --- a/angular-workspace/projects/ni/nimble-angular/CHANGELOG.md +++ b/angular-workspace/projects/ni/nimble-angular/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - @ni/nimble-angular -This log was last generated on Mon, 18 Mar 2024 17:12:34 GMT and should not be manually modified. +This log was last generated on Wed, 20 Mar 2024 16:45:59 GMT and should not be manually modified. +## 20.5.1 + +Wed, 20 Mar 2024 16:45:59 GMT + +### Patches + +- Bump @ni/nimble-components to v22.1.1 + ## 20.5.0 Mon, 18 Mar 2024 17:12:34 GMT diff --git a/angular-workspace/projects/ni/nimble-angular/package.json b/angular-workspace/projects/ni/nimble-angular/package.json index ffb4b78ba8..cc13ba5b80 100644 --- a/angular-workspace/projects/ni/nimble-angular/package.json +++ b/angular-workspace/projects/ni/nimble-angular/package.json @@ -1,6 +1,6 @@ { "name": "@ni/nimble-angular", - "version": "20.5.0", + "version": "20.5.1", "description": "Angular components for the NI Nimble Design System", "scripts": { "invoke-publish": "cd ../../../ && npm run build:library && cd dist/ni/nimble-angular && npm publish" @@ -31,7 +31,7 @@ "@angular/forms": "^15.2.10", "@angular/localize": "^15.2.10", "@angular/router": "^15.2.10", - "@ni/nimble-components": "^22.1.0" + "@ni/nimble-components": "^22.1.1" }, "dependencies": { "tslib": "^2.2.0" diff --git a/package-lock.json b/package-lock.json index 7a23d00821..f8fe26c4e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -74,7 +74,7 @@ }, "angular-workspace/projects/ni/nimble-angular": { "name": "@ni/nimble-angular", - "version": "20.5.0", + "version": "20.5.1", "license": "MIT", "dependencies": { "tslib": "^2.2.0" @@ -85,7 +85,7 @@ "@angular/forms": "^15.2.10", "@angular/localize": "^15.2.10", "@angular/router": "^15.2.10", - "@ni/nimble-components": "^22.1.0" + "@ni/nimble-components": "^22.1.1" } }, "node_modules/@11ty/dependency-tree": { @@ -33939,7 +33939,7 @@ }, "packages/nimble-blazor": { "name": "@ni/nimble-blazor", - "version": "14.5.0", + "version": "14.5.1", "hasInstallScript": true, "license": "MIT", "devDependencies": { @@ -33975,7 +33975,7 @@ }, "packages/nimble-components": { "name": "@ni/nimble-components", - "version": "22.1.0", + "version": "22.1.1", "license": "MIT", "dependencies": { "@microsoft/fast-colors": "^5.3.1", diff --git a/packages/nimble-blazor/package.json b/packages/nimble-blazor/package.json index e804ada040..ff76936429 100644 --- a/packages/nimble-blazor/package.json +++ b/packages/nimble-blazor/package.json @@ -1,6 +1,6 @@ { "name": "@ni/nimble-blazor", - "version": "14.5.0", + "version": "14.5.1", "description": "Blazor components for the NI Nimble Design System", "scripts": { "postinstall": "node build/generate-playwright-version-properties/source/index.js", diff --git a/packages/nimble-components/CHANGELOG.json b/packages/nimble-components/CHANGELOG.json index 2f72e34996..7c3c87ef3a 100644 --- a/packages/nimble-components/CHANGELOG.json +++ b/packages/nimble-components/CHANGELOG.json @@ -1,6 +1,51 @@ { "name": "@ni/nimble-components", "entries": [ + { + "date": "Thu, 21 Mar 2024 00:31:13 GMT", + "version": "22.1.1", + "tag": "@ni/nimble-components_v22.1.1", + "comments": { + "none": [ + { + "author": "7282195+m-akinc@users.noreply.github.com", + "package": "@ni/nimble-components", + "commit": "755823b11215aa2a2c48f296546d0cb8acf6f64c", + "comment": "Small change to design token naming scheme" + } + ] + } + }, + { + "date": "Wed, 20 Mar 2024 16:45:59 GMT", + "version": "22.1.1", + "tag": "@ni/nimble-components_v22.1.1", + "comments": { + "patch": [ + { + "author": "26874831+atmgrifter00@users.noreply.github.com", + "package": "@ni/nimble-components", + "commit": "83f7fd9e601eed789e8f6eb61a9d4a87a1d1835b", + "comment": "Remove ListOptionOwner from Combobox to address issue found in Angular" + } + ] + } + }, + { + "date": "Wed, 20 Mar 2024 15:46:00 GMT", + "version": "22.1.0", + "tag": "@ni/nimble-components_v22.1.0", + "comments": { + "none": [ + { + "author": "20542556+mollykreis@users.noreply.github.com", + "package": "@ni/nimble-components", + "commit": "517e9450043eb0a3797ff278985cc702c4ad0fab", + "comment": "Create anchor patterns story" + } + ] + } + }, { "date": "Mon, 18 Mar 2024 18:10:43 GMT", "version": "22.1.0", diff --git a/packages/nimble-components/CHANGELOG.md b/packages/nimble-components/CHANGELOG.md index 28514fbac5..8825f33110 100644 --- a/packages/nimble-components/CHANGELOG.md +++ b/packages/nimble-components/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - @ni/nimble-components -This log was last generated on Mon, 18 Mar 2024 17:12:34 GMT and should not be manually modified. +This log was last generated on Wed, 20 Mar 2024 16:45:59 GMT and should not be manually modified. +## 22.1.1 + +Wed, 20 Mar 2024 16:45:59 GMT + +### Patches + +- Remove ListOptionOwner from Combobox to address issue found in Angular ([ni/nimble@83f7fd9](https://github.com/ni/nimble/commit/83f7fd9e601eed789e8f6eb61a9d4a87a1d1835b)) + ## 22.1.0 Mon, 18 Mar 2024 17:12:34 GMT diff --git a/packages/nimble-components/CONTRIBUTING.md b/packages/nimble-components/CONTRIBUTING.md index 607712adff..8366356817 100644 --- a/packages/nimble-components/CONTRIBUTING.md +++ b/packages/nimble-components/CONTRIBUTING.md @@ -490,12 +490,13 @@ To modify the generated tokens, complete these steps: Public names for theme-aware tokens are specified in `src/theme-provider/design-token-names.ts`. Use the following structure when creating new tokens. -`[element]-[part]-[state]-[token_type]` +`[element]-[part]-[interaction_states]-[remaining_states]-[token_type]` 1. Where **element** is the type to which the token applies (e.g. 'application', 'body', or 'title-plus-1'). 2. Where **part** is the specific part of the element to which the token applies (e.g. 'border', 'background', or shadow). -3. Where **state** is the more specific state descriptor (e.g. 'selected' or 'disabled'). Multiple states should be sorted alphabetically. -4. Where **token_type** is the token category (e.g. 'color', 'font', 'font-color', 'height', 'width', or 'size'). +3. Where **interaction_states** is one or more interaction states (e.g. 'active', 'disabled', 'hover', or 'selected'). Multiple values should be sorted alphabetically. +4. Where **remaining_states** the remaining, non-interaction states (e.g. 'accent', 'primary, or 'large'). Multiple values should be sorted alphabetically. +5. Where **token_type** is the token category (e.g. 'color', 'font', 'font-color', 'height', 'width', or 'size'). ### Size ramp diff --git a/packages/nimble-components/package.json b/packages/nimble-components/package.json index 946cac8bb2..eedf89911c 100644 --- a/packages/nimble-components/package.json +++ b/packages/nimble-components/package.json @@ -1,6 +1,6 @@ { "name": "@ni/nimble-components", - "version": "22.1.0", + "version": "22.1.1", "description": "Styled web components for the NI Nimble Design System", "scripts": { "build": "npm run generate-icons && npm run generate-workers && npm run build-components && npm run bundle-components && npm run generate-scss && npm run build-storybook", diff --git a/packages/nimble-components/src/combobox/index.ts b/packages/nimble-components/src/combobox/index.ts index 4f607c5439..e3da226ded 100644 --- a/packages/nimble-components/src/combobox/index.ts +++ b/packages/nimble-components/src/combobox/index.ts @@ -17,14 +17,10 @@ import { iconExclamationMarkTag } from '../icons/exclamation-mark'; import { styles } from './styles'; import type { ErrorPattern } from '../patterns/error/types'; -import type { - DropdownPattern, - ListOptionOwner -} from '../patterns/dropdown/types'; +import type { DropdownPattern } from '../patterns/dropdown/types'; import { DropdownAppearance } from '../patterns/dropdown/types'; import type { AnchoredRegion } from '../anchored-region'; import { template } from './template'; -import type { ListOption } from '../list-option'; declare global { interface HTMLElementTagNameMap { @@ -37,7 +33,7 @@ declare global { */ export class Combobox extends FoundationCombobox - implements DropdownPattern, ErrorPattern, ListOptionOwner { + implements DropdownPattern, ErrorPattern { @attr public appearance: DropdownAppearance = DropdownAppearance.underline; @@ -211,23 +207,6 @@ export class Combobox return returnValue; } - /** - * @internal - */ - public registerOption(option: ListOption): void { - if (this.options.includes(option)) { - return; - } - - // Adding an option to the end, ultimately, isn't the correct - // thing to do, as this will mean the option's index in the options, - // at least temporarily, does not match the DOM order. However, it - // is expected that a successive run of `slottedOptionsChanged` will - // correct this order issue. See 'https://github.com/ni/nimble/issues/1915' - // for more info. - this.options.push(option); - } - protected override focusAndScrollOptionIntoView(): void { if (this.open) { super.focusAndScrollOptionIntoView(); diff --git a/packages/nimble-components/src/combobox/tests/combobox.spec.ts b/packages/nimble-components/src/combobox/tests/combobox.spec.ts index 7b282bd515..15973059fc 100644 --- a/packages/nimble-components/src/combobox/tests/combobox.spec.ts +++ b/packages/nimble-components/src/combobox/tests/combobox.spec.ts @@ -9,7 +9,7 @@ import { waitAnimationFrame } from '../../utilities/tests/component'; import { checkFullyInViewport } from '../../utilities/tests/intersection-observer'; -import { ListOption, listOptionTag } from '../../list-option'; +import { listOptionTag } from '../../list-option'; async function setup( position?: string, @@ -127,33 +127,6 @@ describe('Combobox', () => { await disconnect(); }); - it('option added directly to DOM synchronously registers with Combobox', async () => { - const { element, connect, disconnect } = await setup(); - await connect(); - element.selectedIndex = 0; - await waitForUpdatesAsync(); - const newOption = new ListOption('foo', 'foo'); - const registerOptionSpy = spyOn( - element, - 'registerOption' - ).and.callThrough(); - registerOptionSpy.calls.reset(); - element.insertBefore(newOption, element.options[0]!); - - expect(registerOptionSpy.calls.count()).toBe(1); - expect(element.options).toContain(newOption); - - // While the option is registered synchronously as shown above, - // properties like selectedIndex will only be correct asynchronously - // See https://github.com/ni/nimble/issues/1915 - expect(element.selectedIndex).toBe(0); - await waitForUpdatesAsync(); - // This assertion shows that after 'slottedOptionsChanged' runs, the - // 'selectedIndex' state has been corrected to expected DOM order. - expect(element.selectedIndex).toBe(1); - await disconnect(); - }); - const ariaTestData: { attrName: string, propSetter: (x: Combobox, value: string) => void diff --git a/packages/nimble-components/src/patterns/anchor/tests/anchor-patterns.mdx b/packages/nimble-components/src/patterns/anchor/tests/anchor-patterns.mdx new file mode 100644 index 0000000000..9a403b7dfa --- /dev/null +++ b/packages/nimble-components/src/patterns/anchor/tests/anchor-patterns.mdx @@ -0,0 +1,38 @@ +import { Controls, Canvas, Meta, Title } from '@storybook/blocks'; +import * as anchorPatternStories from './anchor-patterns.stories'; + + + + +Anchor components in nimble should behave like native anchors in a number of different ways that are difficult to unit test in an automated manner. +Therefore, this story contains a native anchor element along with all of the nimble components that are anchors or contain anchors. +The following behaviors should be true of the nimble components: + +Mouse interactions: + +- Dragging the nimble component should behave like the native anchor + - Known behavior exceptions: + - Chromium - The drag preview contains the href only, not the label of the anchor. See [chromium issue 329489154](https://issues.chromium.org/issues/329489154). + - Firefox - The drag preview does not contain the label, and in most cases, is blank. See [mozilla bug 1589364](https://bugzilla.mozilla.org/show_bug.cgi?id=1589364). + - Safari - The drag preview contains the href only, not the label of the anchor. +- CTRL + CLICK opens in a new tab (⌘ + CLICK on macOS) +- Right clicking should open the link menu +- Hovering over the component should show the URL preview at the bottom of the browser window +- Copying the link through the right-click link menu should result in the expected string to be copied to the clipboard +- The hover state of the component and mouse pointer should match the area of the component that navigates when clicked (i.e. clicking the white space around the control should not navigate) + +Keyboard interactions: + +- Focusing via tab should show the URL preview at the bottom of the browser window + - Known behavior exceptions: + - Firefox on macOS - Links are not focusable via tab by default. [See this stackoverflow page for how to allow tab focus of links](https://stackoverflow.com/questions/11704828/how-to-allow-keyboard-focus-of-links-in-firefox). + - Safari - Focusing via tab does not show the URL preview at the bottom of the browser, which matches the behavior of native anchors. +- Pressing ENTER when focused should activate the link +- Pressing the MENU KEY on the keyboard while the link is focused should open the same link menu as a right click + +Interactions specific to anchors visualized as text: + +- Selecting the link through click and drag and then copying it should result in the expected string being copied to the clipboard + +<Canvas of={anchorPatternStories.anchorPatterns} /> +<Controls of={anchorPatternStories.anchorPatterns} /> diff --git a/packages/nimble-components/src/patterns/anchor/tests/anchor-patterns.stories.ts b/packages/nimble-components/src/patterns/anchor/tests/anchor-patterns.stories.ts new file mode 100644 index 0000000000..ea0bf6842e --- /dev/null +++ b/packages/nimble-components/src/patterns/anchor/tests/anchor-patterns.stories.ts @@ -0,0 +1,169 @@ +import type { Meta, StoryObj } from '@storybook/html'; +import { html, ref } from '@microsoft/fast-element'; +import { createUserSelectedThemeStory } from '../../../utilities/tests/storybook'; +import { + bodyFont, + bodyFontColor, + controlLabelFont, + controlLabelFontColor, + mediumPadding, + standardPadding +} from '../../../theme-provider/design-tokens'; +import { anchorTag } from '../../../anchor'; +import { anchorButtonTag } from '../../../anchor-button'; +import { anchorTabsTag } from '../../../anchor-tabs'; +import { anchorTabTag } from '../../../anchor-tab'; +import { breadcrumbTag } from '../../../breadcrumb'; +import { breadcrumbItemTag } from '../../../breadcrumb-item'; +import { RichTextViewer, richTextViewerTag } from '../../../rich-text/viewer'; +import { anchorTreeItemTag } from '../../../anchor-tree-item'; +import { treeViewTag } from '../../../tree-view'; +import { anchorMenuItemTag } from '../../../anchor-menu-item'; +import { menuTag } from '../../../menu'; +import { Table, tableTag } from '../../../table'; +import { tableColumnAnchorTag } from '../../../table-column/anchor'; + +interface AnchorPatternsArgs { + label: string; + disabled: boolean; + tableRef: Table; + setTableData: (args: AnchorPatternsArgs) => void; + richTextViewerRef: RichTextViewer; + setRichTextViewerData: (args: AnchorPatternsArgs) => void; +} + +const metadata: Meta<AnchorPatternsArgs> = { + title: 'Tests/Anchor Patterns', + parameters: { + actions: {} + }, + // prettier-ignore + render: createUserSelectedThemeStory(html` + <style class='code-hide'> + .control-container { + margin: var(${standardPadding.cssCustomProperty}); + } + + .label { + font: var(${controlLabelFont.cssCustomProperty}); + color: var(${controlLabelFontColor.cssCustomProperty}); + margin-bottom: var(${mediumPadding.cssCustomProperty}); + } + + .text { + font: var(${bodyFont.cssCustomProperty}); + color: var(${bodyFontColor.cssCustomProperty}); + margin-top: var(${mediumPadding.cssCustomProperty}); + } + </style> + <div class="control-container"> + <div class="label">Native anchor</div> + <a href="${x => (x.disabled ? undefined : 'https://nimble.ni.dev?type=native-anchor-1')}">${x => x.label}</a> + <div class="text">Text that contains a <a href="${x => (x.disabled ? undefined : 'https://nimble.ni.dev?type=native-anchor-2')}">native anchor element</a>.</div> + </div> + + <div class="control-container"> + <div class="label">${anchorTag}</div> + <${anchorTag} href="${x => (x.disabled ? undefined : 'https://nimble.ni.dev?type=nimble-anchor-1')}">${x => x.label}</${anchorTag}> + <div class="text">Text that contains a <${anchorTag} href="${x => (x.disabled ? undefined : 'https://nimble.ni.dev?type=nimble-anchor-2')}">nimble anchor element</${anchorTag}>.</div> + </div> + + <div class="control-container"> + <div class="label">${anchorButtonTag}</div> + <${anchorButtonTag} href="https://nimble.ni.dev?type=nimble-anchor-button" ?disabled="${x => x.disabled}">${x => x.label}</${anchorButtonTag}> + </div> + + <div class="control-container"> + <div class="label">${anchorTabsTag}</div> + <${anchorTabsTag}> + <${anchorTabTag} href="https://nimble.ni.dev?type=nimble-anchor-tab-1" ?disabled="${x => x.disabled}">${x => x.label} - 1</${anchorTabTag}> + <${anchorTabTag} href="https://nimble.ni.dev?type=nimble-anchor-tab-2" ?disabled="${x => x.disabled}">${x => x.label} - 2</${anchorTabTag}> + </${anchorTabsTag}> + </div> + + <div class="control-container"> + <div class="label">${breadcrumbTag}</div> + <${breadcrumbTag}> + <${breadcrumbItemTag} href="${x => (x.disabled ? undefined : 'https://nimble.ni.dev?type=nimble-breadcrumb-item')}">${x => x.label}</${breadcrumbItemTag}> + <${breadcrumbItemTag}>Current page (no link)</${breadcrumbItemTag}> + </${breadcrumbTag}> + </div> + + <div class="control-container"> + <div class="label">${anchorTreeItemTag}</div> + <${treeViewTag}> + <${anchorTreeItemTag} href="https://nimble.ni.dev?type=nimble-anchor-tree-item" ?disabled="${x => x.disabled}">${x => x.label}</${anchorTreeItemTag}> + </${treeViewTag}> + </div> + + <div class="control-container"> + <div class="label">${anchorMenuItemTag}</div> + <${menuTag}> + <${anchorMenuItemTag} href="https://nimble.ni.dev?type=nimble-anchor-menu-item" ?disabled="${x => x.disabled}">${x => x.label}</${anchorMenuItemTag}> + </${menuTag}> + </div> + + <div class="control-container"> + <div class="label">${tableColumnAnchorTag}</div> + <${tableTag} ${ref('tableRef')} data-unused="${x => x.setTableData(x)}" style="height: 100px;"> + <${tableColumnAnchorTag} label-field-name="label" href-field-name="href">Anchor</${tableColumnAnchorTag}> + </${tableTag}> + </div> + + <div class="control-container"> + <div class="label">${richTextViewerTag}</div> + <${richTextViewerTag} ${ref('richTextViewerRef')} + data-unused="${x => x.setRichTextViewerData(x)}" + > + ${x => x.label} + </${richTextViewerTag}> + </div> + `), + argTypes: { + tableRef: { + table: { disable: true } + }, + setTableData: { + table: { disable: true } + }, + richTextViewerRef: { + table: { disable: true } + }, + setRichTextViewerData: { + table: { disable: true } + } + }, + args: { + label: 'link', + disabled: false, + setTableData: x => { + void (async () => { + // Safari workaround: the nimble-table element instance is made at this point + // but doesn't seem to be upgraded to a custom element yet + await customElements.whenDefined('nimble-table'); + const data = [ + { + label: x.label, + href: x.disabled + ? undefined + : 'https://nimble.ni.dev?type=nimble-table-column-anchor' + } + ]; + void x.tableRef.setData(data); + })(); + }, + setRichTextViewerData: x => { + void (async () => { + // Safari workaround: the nimble-rich-text-viewer element instance is made at this point + // but doesn't seem to be upgraded to a custom element yet + await customElements.whenDefined('nimble-rich-text-viewer'); + const data = `Absolute link: <${x.disabled ? '' : 'https://nimble.ni.dev?type=nimble-rich-text-viewer'}>`; + x.richTextViewerRef.markdown = data; + })(); + } + } +}; + +export default metadata; + +export const anchorPatterns: StoryObj<AnchorPatternsArgs> = {}; diff --git a/specs/templates/custom-component.md b/specs/templates/custom-component.md index b4655429d8..dee58d9827 100644 --- a/specs/templates/custom-component.md +++ b/specs/templates/custom-component.md @@ -109,6 +109,7 @@ - *Components which delegate focus require all global ARIA attributes to be enumerated* - *Components should either follow an existing [ARIA Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/) or provide thorough research indicating why a new pattern is appropriate. Research should include sources like [Open UI Community Group](https://github.com/openui/open-ui) and other popular design systems.* - *Behavior with browser configurations like "Prefers reduced motion"* +- *Support for standard link behaviors if the component is an anchor or contains an anchor. These behaviors are enumerated in the [anchor-patterns story](/packages/nimble-components/src/patterns/anchor/tests/anchor-patterns.mdx). The story should be updated to include the new component.* ### Mobile diff --git a/specs/templates/fast-based-component.md b/specs/templates/fast-based-component.md index e1acfeba43..3bd79913da 100644 --- a/specs/templates/fast-based-component.md +++ b/specs/templates/fast-based-component.md @@ -57,6 +57,7 @@ - *Documentation: Any requirements besides standard Storybook docs and updating the Example Client App demo?* - *Tooling: Any new tools, updates to tools, code generation, etc?* - *Accessibility: keyboard navigation/focus, form input, use with assistive technology, etc.* + - *Support for standard link behaviors if the component is an anchor or contains an anchor. These behaviors are enumerated in the [anchor-patterns story](/packages/nimble-components/src/patterns/anchor/tests/anchor-patterns.mdx). The story should be updated to include the new component.* - *Mobile: small screens, touch interactions, mobile-specific integrations* - *Globalization: special RTL handling, swapping of icons/visuals, localization, etc.* - *Performance: does the FAST component meet Nimble's performance requirements?*