From d001a4208087cf7663d1cf09323bf7163bb1c526 Mon Sep 17 00:00:00 2001 From: Mikko Korhonen Date: Thu, 15 Aug 2024 16:45:37 +0300 Subject: [PATCH] fix: label related accessibility issues with FavoriteEntity Signed-off-by: Mikko Korhonen --- .changeset/twelve-peaches-develop.md | 5 + plugins/catalog-react/api-report.md | 1 + .../MockStarredEntitiesApi.ts | 4 + .../FavoriteEntity/FavoriteEntity.test.tsx | 108 ++++++++++++++++++ .../FavoriteEntity/FavoriteEntity.tsx | 22 ++-- .../TemplateCard/CardHeader.test.tsx | 2 +- 6 files changed, 132 insertions(+), 10 deletions(-) create mode 100644 .changeset/twelve-peaches-develop.md create mode 100644 plugins/catalog-react/src/components/FavoriteEntity/FavoriteEntity.test.tsx diff --git a/.changeset/twelve-peaches-develop.md b/.changeset/twelve-peaches-develop.md new file mode 100644 index 0000000000..804c352ba4 --- /dev/null +++ b/.changeset/twelve-peaches-develop.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +Fix label related accessibility issues with `FavorityEntity` diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md index 79aad53c33..b440701057 100644 --- a/plugins/catalog-react/api-report.md +++ b/plugins/catalog-react/api-report.md @@ -686,6 +686,7 @@ export function MockEntityListContextProvider< // @public export class MockStarredEntitiesApi implements StarredEntitiesApi { + constructor(opts?: { starredEntities?: string[] }); // (undocumented) starredEntitie$(): Observable>; // (undocumented) diff --git a/plugins/catalog-react/src/apis/StarredEntitiesApi/MockStarredEntitiesApi.ts b/plugins/catalog-react/src/apis/StarredEntitiesApi/MockStarredEntitiesApi.ts index 9467153dd9..ed60051026 100644 --- a/plugins/catalog-react/src/apis/StarredEntitiesApi/MockStarredEntitiesApi.ts +++ b/plugins/catalog-react/src/apis/StarredEntitiesApi/MockStarredEntitiesApi.ts @@ -29,6 +29,10 @@ export class MockStarredEntitiesApi implements StarredEntitiesApi { ZenObservable.SubscriptionObserver> >(); + constructor(opts: { starredEntities?: string[] } = {}) { + this.starredEntities = new Set(opts.starredEntities); + } + private readonly observable = new ObservableImpl>(subscriber => { subscriber.next(new Set(this.starredEntities)); diff --git a/plugins/catalog-react/src/components/FavoriteEntity/FavoriteEntity.test.tsx b/plugins/catalog-react/src/components/FavoriteEntity/FavoriteEntity.test.tsx new file mode 100644 index 0000000000..a166d0a15d --- /dev/null +++ b/plugins/catalog-react/src/components/FavoriteEntity/FavoriteEntity.test.tsx @@ -0,0 +1,108 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { storageApiRef } from '@backstage/core-plugin-api'; + +import { MockStarredEntitiesApi, starredEntitiesApiRef } from '../../apis'; +import { FavoriteEntity } from './FavoriteEntity'; +import { ComponentEntity } from '@backstage/catalog-model'; +import { + MockStorageApi, + renderInTestApp, + TestApiProvider, +} from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +const entity: ComponentEntity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'example', + }, + spec: { + type: 'service', + lifecycle: 'experimental', + owner: 'user:default/john.doe_example.com', + }, +}; + +const mockStorage = MockStorageApi.create(); + +describe('', () => { + it('should add to favorites', async () => { + await renderInTestApp( + + + , + ); + + const addToFavorite = screen.getByRole('button', { + name: 'Add to favorites', + }); + + // Should keep the label when hovering + await userEvent.hover(addToFavorite); + expect(addToFavorite).toBeInTheDocument(); + + await userEvent.click(addToFavorite); + expect( + screen.getByRole('button', { + name: 'Remove from favorites', + }), + ).toBeInTheDocument(); + }); + + it('should remove from favorites', async () => { + await renderInTestApp( + + + , + ); + + const removeFromFavorites = screen.getByRole('button', { + name: 'Remove from favorites', + }); + + // Should keep the label when hovering + await userEvent.hover(removeFromFavorites); + expect(removeFromFavorites).toBeInTheDocument(); + + await userEvent.click(removeFromFavorites); + + expect( + screen.getByRole('button', { + name: 'Add to favorites', + }), + ).toBeInTheDocument(); + }); +}); diff --git a/plugins/catalog-react/src/components/FavoriteEntity/FavoriteEntity.tsx b/plugins/catalog-react/src/components/FavoriteEntity/FavoriteEntity.tsx index 8848857976..ab674ff98e 100644 --- a/plugins/catalog-react/src/components/FavoriteEntity/FavoriteEntity.tsx +++ b/plugins/catalog-react/src/components/FavoriteEntity/FavoriteEntity.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; +import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import IconButton from '@material-ui/core/IconButton'; import Tooltip from '@material-ui/core/Tooltip'; import { withStyles } from '@material-ui/core/styles'; @@ -46,20 +46,24 @@ export const FavoriteEntity = (props: FavoriteEntityProps) => { props.entity, ); const { t } = useTranslationRef(catalogReactTranslationRef); + const title = isStarredEntity + ? t('favoriteEntity.removeFromFavorites') + : t('favoriteEntity.addToFavorites'); + + const id = `favorite-${stringifyEntityRef(props.entity).replace( + /[^a-zA-Z0-9-_]/g, + '-', + )}`; + return ( toggleStarredEntity()} > - + {isStarredEntity ? : } diff --git a/plugins/scaffolder-react/src/next/components/TemplateCard/CardHeader.test.tsx b/plugins/scaffolder-react/src/next/components/TemplateCard/CardHeader.test.tsx index 90157c3226..58033b718c 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateCard/CardHeader.test.tsx +++ b/plugins/scaffolder-react/src/next/components/TemplateCard/CardHeader.test.tsx @@ -119,7 +119,7 @@ describe('CardHeader', () => { , ); - const favorite = getByRole('button', { name: 'favorite' }); + const favorite = getByRole('button', { name: 'Add to favorites' }); await fireEvent.click(favorite);