Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
61d83cd
Accessiblity issues: empty links and buttons
labkey-susanh Apr 24, 2026
551f11b
v7.33.1-emptyLinksAndButtons.0
labkey-susanh Apr 24, 2026
1b35ed2
Add IconWithSrText component for use on buttons and links that are on…
labkey-susanh Apr 30, 2026
b538cb7
merge from develop and update package version
labkey-susanh Apr 30, 2026
4e3bdc3
Add display name
labkey-susanh Apr 30, 2026
19f71ee
Switch back to button with new IconWithSrText as child
labkey-susanh Apr 30, 2026
195e759
Switch back to button with new IconWithSrText as child
labkey-susanh Apr 30, 2026
c3b2102
Restore disabled property
labkey-susanh Apr 30, 2026
943776d
Update tests
labkey-susanh Apr 30, 2026
d64dcd8
@labkey/components v7.33.3-emptyLinksAndButtons.1
labkey-susanh Apr 30, 2026
ecdee54
IconWithSrText -> Icon
labkey-susanh Apr 30, 2026
c927a94
@labkey/components v7.33.3-emptyLinksAndButtons.2
labkey-susanh Apr 30, 2026
1cbbe81
Add alt text for some images
labkey-susanh Apr 30, 2026
813f87a
@labkey/components v7.33.3-emptyLinksAndButtons.3
labkey-susanh Apr 30, 2026
6f7e59f
Update release note
labkey-susanh Apr 30, 2026
cc3b709
@labkey/components v7.33.3-emptyLinksAndButtons.4 and @labkey/premium…
labkey-susanh Apr 30, 2026
a16e478
Hide icon from screen readers to improve accessibility
labkey-susanh Apr 30, 2026
4d60f27
Don't use `<Icon>` since there's already text
labkey-susanh Apr 30, 2026
420bb94
Add back button type attribute
labkey-susanh Apr 30, 2026
4d85036
Use aria-label instead of sr-only for less redundancy and leakage of …
labkey-susanh Apr 30, 2026
d2d7315
@labkey/components v7.33.3-emptyLinksAndButtons.5
labkey-susanh Apr 30, 2026
828644b
Move Icon component to its own file
labkey-susanh Apr 30, 2026
c3c3978
Conditional alt text
labkey-susanh Apr 30, 2026
b72540d
@labkey/components v7.33.3-emptyLinksAndButtons.6
labkey-susanh Apr 30, 2026
09b6742
Remove falsy check for srText in Icon component
labkey-susanh May 1, 2026
b46c1d9
Use title in alt text if caption is not provided
labkey-susanh May 1, 2026
5bbfc35
@labkey/components v7.33.3-emptyLinksAndButtons.7
labkey-susanh May 1, 2026
6a0f4d7
Add aria-label for non-anchor dropdown titles
labkey-susanh May 1, 2026
5351138
@labkey/components v7.33.3-emptyLinksAndButtons.8
labkey-susanh May 1, 2026
884641f
Merge from develop
labkey-susanh May 6, 2026
4285693
@labkey/components v7.34.1-emptyLinksAndButtons.9
labkey-susanh May 6, 2026
92bcd48
Add some missing labels for icon menus and links
labkey-susanh May 6, 2026
8f732e9
@labkey/components v7.34.1-emptyLinksAndButtons.10
labkey-susanh May 6, 2026
0d4b718
hide icon for aria instead of adding redundant text
labkey-susanh May 6, 2026
b032291
@labkey/components v7.34.1-emptyLinksAndButtons.11
labkey-susanh May 6, 2026
433848c
Aria-label instead
labkey-susanh May 6, 2026
d9283ec
Update snapshots
labkey-susanh May 6, 2026
b47417b
@labkey/components v7.34.1-emptyLinksAndButtons.12
labkey-susanh May 6, 2026
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
4 changes: 2 additions & 2 deletions packages/components/package-lock.json

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

2 changes: 1 addition & 1 deletion packages/components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@labkey/components",
"version": "7.34.0",
"version": "7.34.1-emptyLinksAndButtons.12",
"description": "Components, models, actions, and utility functions for LabKey applications and pages",
"sideEffects": false,
"files": [
Expand Down
6 changes: 6 additions & 0 deletions packages/components/releaseNotes/components.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# @labkey/components
Components, models, actions, and utility functions for LabKey applications and pages

### version TBD
*Released*: TBD
- Fix accessibility issues for empty links and buttons
- Add `Icon` component for use on buttons and links that are only icons
- Add some alt text to a few images

### version 7.34.0
*Released*: 5 May 2026
- Accessibility improvements for app pages: Colors
Expand Down
2 changes: 2 additions & 0 deletions packages/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -891,6 +891,7 @@ import { OverlayTrigger, useOverlayTriggerState } from './internal/OverlayTrigge
import { Tooltip } from './internal/Tooltip';
import { Popover } from './internal/Popover';
import { DropdownButton, DropdownMenu, MenuDivider, MenuHeader, MenuItem, SplitButton } from './internal/dropdowns';
import { Icon } from './internal/Icon';
import { DropdownSection } from './internal/DropdownSection';
import { isLoginAutoRedirectEnabled, showPremiumFeatures } from './internal/components/administration/utils';
import { LineageGridModel, LineageResult } from './internal/components/lineage/models';
Expand Down Expand Up @@ -1464,6 +1465,7 @@ export {
HOME_TITLE,
Hooks,
HorizontalBarSection,
Icon,
imageURL,
IMPORT_DATA_FORM_TYPES,
ImportAliasRenderer,
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/internal/DropdownSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const DropdownSection: FC<MenuSectionProps> = ({ items, showDivider = fal
)}

{expanded && items.length > SHOW_FILTER_CUTOFF && (
<MenuItem>
<MenuItem aria-label="Filter selection options">
<input
aria-label="Filter selection options"
onChange={onFilterChange}
Expand Down
15 changes: 15 additions & 0 deletions packages/components/src/internal/Icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React, { FC } from 'react';

interface IconProps {
iconClass: string;
srText: string; // text for screen readers
}
export const Icon: FC<IconProps> = ({ iconClass, srText }) => {
return (
<>
<span aria-hidden="true" className={iconClass} />
<span className="sr-only">{srText}</span>
</>
);
};
Icon.displayName = 'Icon';
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ const ThreadBlockHeader: FC<ThreadBlockHeaderProps> = props => {
{(onDelete || onEdit) && (
<DropdownMenu
className="thread-block-header__menu"
label={"Manage thread block"}
title={<i className="fa fa-ellipsis-v" />}
pullRight
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ const ThreadEditorToolbar: FC<ThreadEditorToolbarProps> = memo(({ inputRef, setB
return (
<div className="thread-editor-toolbar editor-toolbar">
<div className="editor-toolbar__section insert-menu">
<DropdownMenu title={view}>
<DropdownMenu label="Editor View" title={view}>
<MenuItem onClick={setEditMode}>{EditorView.edit}</MenuItem>
<MenuItem onClick={setPreviewMode}>{EditorView.preview}</MenuItem>
</DropdownMenu>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ describe('Cards', () => {
{ title: 'card1' },
{ title: 'card2', caption: 'caption2' },
{ title: 'card3', iconSrc: 'iconSrc' },
{ title: 'card4', iconUrl: 'iconUrl' },
{ title: 'card4', iconUrl: 'iconUrl4' },
{ title: 'card5', disabled: true },
{ title: 'card6', href: 'href' },
{ title: 'card7', onClick: jest.fn() },
{
title: 'all',
caption: 'captionAll',
iconSrc: 'iconSrc',
iconUrl: 'iconUrl',
iconUrl: 'iconUrlAll',
href: 'href',
disabled: true,
onClick: jest.fn(),
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/internal/components/base/Cards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const Card: FC<CardProps> = props => {
<a className="cards__card" href={href} onClick={onClickHandler}>
<div className={'cards__block-center' + (disabled ? ' cards__block-disabled' : '')}>
<div className="cards__block-center-content">
{iconUrl && <img src={iconUrl} />}
{iconUrl && <img alt={caption ? caption + ' icon' : title + ' icon'} src={iconUrl} />}
{iconSrc && <SVGIcon iconSrc={iconSrc} />}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const HorizontalBarSection: FC<Props> = memo(props => {
})}
>
{row.href && (
<a href={row.href} className="horizontal-bar--link">
<a aria-label={row.title} className="horizontal-bar--link" href={row.href}>
&nbsp;
</a>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ exports[`SampleTypePropertiesPanel appPropertiesOnly 1`] = `
data-name="labelColor"
>
<button
aria-label="Label color picker"
class="color-picker__button btn btn-default"
type="button"
>
Expand Down Expand Up @@ -519,6 +520,7 @@ exports[`SampleTypePropertiesPanel appPropertiesOnly 1`] = `
data-name="labelColor"
>
<button
aria-label="Label color picker"
class="color-picker__button btn btn-default"
type="button"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ export class PageDetailHeader extends PureComponent<PageDetailHeaderProps> {
{hasIcon && (
<div className="detail__header--image-container">
{iconUrl ? (
<img className="detail__header-icon" src={iconUrl} />
<img
alt={iconAltText ? iconAltText : ''}
className="detail__header-icon"
src={iconUrl}
/>
) : (
<SVGIcon
alt={iconAltText ? iconAltText : ''}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export const ColorPickerInput: FC<Props> = memo(props => {
return (
<div className="color-picker" data-name={name}>
<button
aria-label="Label color picker"
className="color-picker__button btn btn-default"
disabled={disabled}
onClick={togglePicker}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ exports[`ColorPickerInput allowRemove 1`] = `
class="color-picker"
>
<button
aria-label="Label color picker"
class="color-picker__button btn btn-default"
type="button"
>
Expand Down Expand Up @@ -41,6 +42,7 @@ exports[`ColorPickerInput default props 1`] = `
class="color-picker"
>
<button
aria-label="Label color picker"
class="color-picker__button btn btn-default"
type="button"
>
Expand All @@ -65,6 +67,7 @@ exports[`ColorPickerInput disabled 1`] = `
class="color-picker"
>
<button
aria-label="Label color picker"
class="color-picker__button btn btn-default"
disabled=""
type="button"
Expand All @@ -90,6 +93,7 @@ exports[`ColorPickerInput showPicker 1`] = `
class="color-picker"
>
<button
aria-label="Label color picker"
class="color-picker__button btn btn-default"
type="button"
>
Expand Down Expand Up @@ -871,6 +875,7 @@ exports[`ColorPickerInput with button text 1`] = `
class="color-picker"
>
<button
aria-label="Label color picker"
class="color-picker__button btn btn-default"
type="button"
>
Expand All @@ -896,6 +901,7 @@ exports[`ColorPickerInput without value 1`] = `
class="color-picker"
>
<button
aria-label="Label color picker"
class="color-picker__button btn btn-default"
type="button"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { PureComponent, ReactNode } from 'react';
import { Network } from 'vis-network';

import { DropdownButton, MenuItem } from '../../../dropdowns';
import { Icon } from '../../../Icon';

const PAN_INCREMENT = 20;
const ZOOM_INCREMENT = 0.05;
Expand Down Expand Up @@ -71,10 +72,10 @@ export class VisGraphControls extends PureComponent<GraphControlsProps> {
<div className="lineage-visgraph-control-zoom">
<div className="btn-group">
<button className="btn btn-default" onClick={this.zoomOut} type="button">
<i className="fa fa-search-minus" />
<Icon iconClass="fa fa-search-minus" srText="Zoom out" />
</button>
<button className="btn btn-default" onClick={this.zoomIn} type="button">
<i className="fa fa-search-plus" />
<Icon iconClass="fa fa-search-plus" srText="Zoom in" />
</button>
</div>
</div>
Expand All @@ -84,17 +85,17 @@ export class VisGraphControls extends PureComponent<GraphControlsProps> {
onClick={this.panUp}
type="button"
>
<i className="fa fa-arrow-up" />
<Icon iconClass="fa fa-arrow-up" srText="Pan up" />
</button>
<div className="btn-group">
<button className="btn btn-default" onClick={this.panLeft} type="button">
<i className="fa fa-arrow-left" />
<Icon iconClass="fa fa-arrow-left" srText="Pan left" />
</button>
<button className="btn btn-default" onClick={this.panDown} type="button">
<i className="fa fa-arrow-down" />
<Icon iconClass="fa fa-arrow-down" srText="Pan down" />
</button>
<button className="btn btn-default" onClick={this.panRight} type="button">
<i className="fa fa-arrow-right" />
<Icon iconClass="fa fa-arrow-right" srText="Pan right" />
</button>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Tip } from '../base/Tip';
import { ExpandableContainer } from '../ExpandableContainer';
import { AppLink } from '../../url/AppLink';
import { ADMIN_KEY } from '../../app/constants';
import { Icon } from '../../Icon';

export interface FolderMenuItem {
archived: boolean;
Expand Down Expand Up @@ -65,13 +66,13 @@ export const FolderMenuItems: FC<FolderMenuProps> = memo(props => {
>
<AppLink to={dashboardURL} className="dashboard-link">
<Tip caption="Dashboard">
<i className="fa fa-home dashboard-icon" />
<Icon iconClass="fa fa-home dashboard-icon" srText="Dashboard" />
</Tip>
</AppLink>
{user.isAdmin && (
<AppLink to={adminURL}>
<Tip caption="Administration">
<i className="fa fa-gear" />
<Icon iconClass="fa fa-gear" srText="Administration" />
</Tip>
</AppLink>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { UserMenuGroup, UserMenuProps } from './UserMenuGroup';
import { MenuSectionConfig } from './model';
import { SEARCH_PLACEHOLDER } from './constants';
import { useFolderMenuContext, useSubNavTabsContext } from './hooks';
import { Icon } from '../../Icon';

interface NavigationBarProps {
brand?: ReactNode;
Expand Down Expand Up @@ -136,7 +137,12 @@ export const NavigationBar: FC<Props> = memo(props => {
{onFindByIds ? (
<FindAndSearchDropdown
className="navbar__xs-find-dropdown"
title={<i className="fa fa-search navbar__xs-search-icon" />}
title={
<Icon
iconClass="fa fa-search navbar__xs-search-icon"
srText="Search and Find"
/>
}
findNounPlural="samples"
onSearch={onSearchIconClick}
onFindByIds={onFindByIds}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { NavItem, ParentNavItem } from './NavItem';
import { isAdminRoute } from './ProductMenu';
import { ITab } from './types';
import { useSubNavTabsContext } from './hooks';
import { Icon } from '../../Icon';

interface Props {
noun?: ITab;
Expand Down Expand Up @@ -108,10 +109,10 @@ const SubNavImpl: FC<Props> = ({ noun, tabs }) => {
{isScrollable && (
<div className="btn-group scroll-btn-group">
<button className="btn btn-default" onClick={scrollLeft} type="button">
<i className="fa fa-chevron-left" />
<Icon iconClass="fa fa-chevron-left" srText="Scroll left" />
</button>
<button className="btn btn-default" onClick={scrollRight} type="button">
<i className="fa fa-chevron-right" />
<Icon iconClass="fa fa-chevron-right" srText="Scroll right" />
</button>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@ import { getHelpLink } from '../../util/helpLinks';

import { HELP_LINK_METRIC, RELEASE_NOTES_METRIC } from '../productnavigation/constants';

import { DropdownMenu, DropdownButton, MenuDivider, MenuHeader, MenuItem } from '../../dropdowns';
import { DropdownButton, DropdownMenu, MenuDivider, MenuHeader, MenuItem } from '../../dropdowns';

import { signIn as defaultSignIn, signOut as defaultSignOut } from './actions';
import { MenuSectionModel } from './model';
import { biologicsIsPrimaryApp } from '../../app/products';
import { Icon } from '../../Icon';

export interface UserMenuProps {
appProperties?: AppProperties;
Expand Down Expand Up @@ -130,7 +131,7 @@ export const UserMenuGroupImpl: FC<UserMenuProps & ImplProps> = props => {
return (
<>
<div className="navbar-item pull-right">
<DropdownMenu className="user-dropdown" title={userToggle} pullRight>
<DropdownMenu className="user-dropdown" label="User menu" pullRight title={userToggle} >
<div className="navbar-connector" />
{userMenuItems}
{extraUserItems}
Expand All @@ -147,7 +148,7 @@ export const UserMenuGroupImpl: FC<UserMenuProps & ImplProps> = props => {
<DropdownButton
className="admin-dropdown"
buttonClassName="navbar-menu-button"
title={<i className="fa fa-cog navbar-header-icon" />}
title={<Icon iconClass="fa fa-cog navbar-header-icon" srText="Admin"/>}
noCaret
pullRight
>
Expand All @@ -169,11 +170,11 @@ export const UserMenuGroupImpl: FC<UserMenuProps & ImplProps> = props => {
{(!!helpHref || !!releaseNoteHref) && (
<div className="navbar-item pull-right navbar-item__dropdown">
<DropdownButton
className="help-dropdown"
buttonClassName="navbar-menu-button"
title={<i className="fa fa-question-circle navbar-header-icon" />}
className="help-dropdown"
noCaret
pullRight
title={<Icon iconClass="fa fa-question-circle navbar-header-icon" srText="Help" />}
>
<div className="navbar-icon-connector" />
{helpHref && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe('ServerNotifications', () => {
fireEvent.click(screen.getByRole('button'));

expect(container.querySelector('.server-notifications-listing-container')).toBeInTheDocument();
expect(screen.getByText('Notifications')).toBeInTheDocument();
expect(screen.getAllByText('Notifications')).toHaveLength(2);
expect(container.querySelector('.badge')).not.toBeInTheDocument();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useNavMenuState } from '../../useNavMenuState';
import { markAllNotificationsAsRead, markNotificationsAsRead } from './actions';
import { ServerNotificationsConfig } from './model';
import { ServerActivityList } from './ServerActivityList';
import { Icon } from '../../Icon';

export const ServerNotifications: FC<ServerNotificationsConfig> = props => {
const { onRead, serverActivity } = props;
Expand Down Expand Up @@ -78,7 +79,7 @@ export const ServerNotifications: FC<ServerNotificationsConfig> = props => {
role="button"
type="button"
>
<span className={iconClassName} />
<Icon iconClass={iconClassName} srText="Notifications" />
{unreadCount > 0 && <span className="badge">{unreadCount}</span>}
</button>

Expand Down
Loading