]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-22102 Fixing missing udpate of project tags update (#11002)
authorMathieu Suen <59278745+mathieu-suen-sonarsource@users.noreply.github.com>
Mon, 22 Apr 2024 12:31:11 +0000 (14:31 +0200)
committersonartech <sonartech@sonarsource.com>
Mon, 22 Apr 2024 20:02:38 +0000 (20:02 +0000)
server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaTags.tsx
server/sonar-web/src/main/js/apps/projectInformation/about/components/__tests__/MetaTags-test.tsx

index 7d43bf16fd17984e1cd0c7d4bf32f377389b032e..81653411f406aa3c7d1b6897d664225fcc377f4b 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import { Spinner } from '@sonarsource/echoes-react';
 import { MultiSelector, SubHeading, Tags } from 'design-system';
 import { difference, without } from 'lodash';
-import React, { useState } from 'react';
+import React, { useEffect, useState } from 'react';
 import { searchProjectTags, setApplicationTags, setProjectTags } from '../../../../api/components';
 import Tooltip from '../../../../components/controls/Tooltip';
 import { PopupPlacement } from '../../../../components/ui/popups';
@@ -33,7 +34,11 @@ interface Props {
 }
 
 export default function MetaTags(props: Props) {
-  const [open, setOpen] = React.useState(false);
+  const [loading, setLoading] = useState(false);
+
+  useEffect(() => {
+    setLoading(false);
+  }, [props.component.tags]);
 
   const canUpdateTags = () => {
     const { configuration } = props.component;
@@ -57,6 +62,7 @@ export default function MetaTags(props: Props) {
   };
 
   const handleSetProjectTags = (values: string[]) => {
+    setLoading(true);
     setTags(values).then(
       () => props.onComponentChange({ tags: values }),
       () => {},
@@ -73,13 +79,15 @@ export default function MetaTags(props: Props) {
         ariaTagsListLabel={translate('tags')}
         className="project-info-tags"
         emptyText={translate('no_tags')}
-        overlay={<MetaTagsSelector selectedTags={tags} setProjectTags={handleSetProjectTags} />}
+        overlay={
+          <Spinner isLoading={loading}>
+            <MetaTagsSelector selectedTags={tags} setProjectTags={handleSetProjectTags} />
+          </Spinner>
+        }
         popupPlacement={PopupPlacement.Bottom}
         tags={tags}
         tagsToDisplay={2}
         tooltip={Tooltip}
-        open={open}
-        onClose={() => setOpen(false)}
       />
     </>
   );
index fea012025a3ff4950e84854529138cfd78a436f0..8267fa68ff1c54ffbdf3cb19feef977ae764925f 100644 (file)
@@ -24,6 +24,7 @@ import { setApplicationTags, setProjectTags } from '../../../../../api/component
 import { mockComponent } from '../../../../../helpers/mocks/component';
 import { renderComponent } from '../../../../../helpers/testReactTestingUtils';
 import { ComponentQualifier } from '../../../../../types/component';
+import { Component } from '../../../../../types/types';
 import MetaTags from '../MetaTags';
 
 jest.mock('../../../../../api/components', () => ({
@@ -45,8 +46,6 @@ it('should render without tags and admin rights', async () => {
 
 it('should allow to edit tags for a project', async () => {
   const user = userEvent.setup();
-
-  const onComponentChange = jest.fn();
   const component = mockComponent({
     key: 'my-second-project',
     tags: ['foo', 'bar'],
@@ -56,7 +55,7 @@ it('should allow to edit tags for a project', async () => {
     name: 'MySecondProject',
   });
 
-  renderMetaTags({ component, onComponentChange });
+  renderMetaTags(component);
 
   expect(await screen.findByText('foo, bar')).toBeInTheDocument();
   expect(screen.getByRole('button')).toBeInTheDocument();
@@ -66,15 +65,11 @@ it('should allow to edit tags for a project', async () => {
   expect(await screen.findByRole('checkbox', { name: 'best' })).toBeInTheDocument();
 
   await user.click(screen.getByRole('checkbox', { name: 'best' }));
-  expect(onComponentChange).toHaveBeenCalledWith({ tags: ['foo', 'bar', 'best'] });
-
-  onComponentChange.mockClear();
+  expect(await screen.findByRole('button', { name: 'foo bar ... +' })).toBeInTheDocument();
 
-  /*
-   * Since we're not actually updating the tags, we're back to having the foo, bar only
-   */
   await user.click(screen.getByRole('checkbox', { name: 'bar' }));
-  expect(onComponentChange).toHaveBeenCalledWith({ tags: ['foo'] });
+
+  expect(await screen.findByRole('button', { name: 'foo best +' })).toBeInTheDocument();
 
   expect(setProjectTags).toHaveBeenCalled();
   expect(setApplicationTags).not.toHaveBeenCalled();
@@ -85,14 +80,14 @@ it('should allow to edit tags for a project', async () => {
 it('should set tags for an app', async () => {
   const user = userEvent.setup();
 
-  renderMetaTags({
-    component: mockComponent({
+  renderMetaTags(
+    mockComponent({
       configuration: {
         showSettings: true,
       },
       qualifier: ComponentQualifier.Application,
     }),
-  });
+  );
 
   await user.click(screen.getByRole('button', { name: 'no_tags +' }));
 
@@ -102,14 +97,25 @@ it('should set tags for an app', async () => {
   expect(setApplicationTags).toHaveBeenCalled();
 });
 
-function renderMetaTags(overrides: Partial<Parameters<typeof MetaTags>[0]> = {}) {
-  const component = mockComponent({
-    configuration: {
-      showSettings: false,
-    },
-  });
-
-  return renderComponent(
-    <MetaTags component={component} onComponentChange={jest.fn()} {...overrides} />,
-  );
+function renderMetaTags(componentOverride: Partial<Component> = {}) {
+  function Component(componentOverride: Partial<Parameters<typeof MetaTags>[0]>) {
+    const [component, setComponent] = React.useState(
+      mockComponent({
+        configuration: {
+          showSettings: false,
+        },
+        ...componentOverride,
+      }),
+    );
+
+    const handleComponentChange = ({ tags }: { tags: string[] }) => {
+      setComponent((c) => {
+        return { ...c, tags };
+      });
+    };
+
+    return <MetaTags component={component} onComponentChange={handleComponentChange} />;
+  }
+
+  return renderComponent(<Component {...componentOverride} />);
 }