]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-16789, SONAR-16965 Group of checkboxes is missing <fieldset>
authorMathieu Suen <mathieu.suen@sonarsource.com>
Thu, 25 Aug 2022 08:40:04 +0000 (10:40 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 26 Aug 2022 20:03:24 +0000 (20:03 +0000)
server/sonar-web/src/main/js/components/activity-graph/AddGraphMetricPopup.tsx
server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/AddGraphMetricPopup-test.tsx.snap
server/sonar-web/src/main/js/components/common/MultiSelect.tsx
server/sonar-web/src/main/js/components/common/__tests__/MultiSelect-test.tsx
server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelect-test.tsx.snap
server/sonar-web/src/main/js/components/tags/TagsSelector.tsx
server/sonar-web/src/main/js/components/tags/__tests__/__snapshots__/TagsSelector-test.tsx.snap
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 6b0b8d235526433d534a92e82774500e82f720bc..5e7f78e6e44b4956f419f7ec4e4bf28152d157b3 100644 (file)
@@ -69,10 +69,11 @@ export default function AddGraphMetricPopup({
         elements={elements}
         filterSelected={props.filterSelected}
         footerNode={footerNode}
+        legend={translate('project_activity.graphs.custom.select_metric')}
         onSearch={props.onSearch}
         onSelect={(item: string) => elements.includes(item) && props.onSelect(item)}
         onUnselect={props.onUnselect}
-        placeholder={translate('search.search_for_tags')}
+        placeholder={translate('search.search_for_metrics')}
         renderLabel={props.renderLabel}
         selectedElements={props.selectedElements}
       />
index f914a1314a5bb4bed1466ccd2bd1cf5f1d7fc6dd..3070ede97610c29c1489cc792ad10d7d8bd2628e 100644 (file)
@@ -10,11 +10,12 @@ exports[`should render correctly 1`] = `
     elements={Array []}
     filterSelected={[MockFunction]}
     footerNode=""
+    legend="project_activity.graphs.custom.select_metric"
     listSize={0}
     onSearch={[MockFunction]}
     onSelect={[Function]}
     onUnselect={[MockFunction]}
-    placeholder="search.search_for_tags"
+    placeholder="search.search_for_metrics"
     renderLabel={[Function]}
     selectedElements={Array []}
     validateSearchInput={[Function]}
@@ -39,11 +40,12 @@ exports[`should render correctly whith 6+ selected elements 1`] = `
         project_activity.graphs.custom.add_metric_info
       </Alert>
     }
+    legend="project_activity.graphs.custom.select_metric"
     listSize={0}
     onSearch={[MockFunction]}
     onSelect={[Function]}
     onUnselect={[MockFunction]}
-    placeholder="search.search_for_tags"
+    placeholder="search.search_for_metrics"
     renderLabel={[Function]}
     selectedElements={
       Array [
@@ -77,11 +79,12 @@ exports[`should render correctly with type filter 1`] = `
         project_activity.graphs.custom.type_x_message.metric.type.filter1, metric.type.filter2
       </Alert>
     }
+    legend="project_activity.graphs.custom.select_metric"
     listSize={0}
     onSearch={[MockFunction]}
     onSelect={[Function]}
     onUnselect={[MockFunction]}
-    placeholder="search.search_for_tags"
+    placeholder="search.search_for_metrics"
     renderLabel={[Function]}
     selectedElements={Array []}
     validateSearchInput={[Function]}
index 942721bf72830da65b6395305cd28cf0cb6c26fe..703df7b833ff35dd758bf9c797506e08bdfeb3cd 100644 (file)
@@ -29,6 +29,7 @@ import MultiSelectOption from './MultiSelectOption';
 export interface MultiSelectProps {
   allowNewElements?: boolean;
   allowSelection?: boolean;
+  legend: string;
   elements: string[];
   // eslint-disable-next-line react/no-unused-prop-types
   filterSelected?: (query: string, selectedElements: string[]) => string[];
@@ -258,7 +259,7 @@ export default class MultiSelect extends React.PureComponent<PropsWithDefault, S
   };
 
   render() {
-    const { allowSelection = true, allowNewElements = true, footerNode = '' } = this.props;
+    const { legend, allowSelection = true, allowNewElements = true, footerNode = '' } = this.props;
     const { renderLabel } = this.props as PropsWithDefault;
     const { query, activeIdx, selectedElements, unselectedElements } = this.state;
     const activeElement = this.getAllElements(this.props, this.state)[activeIdx];
@@ -283,46 +284,48 @@ export default class MultiSelect extends React.PureComponent<PropsWithDefault, S
             value={query}
           />
         </div>
-        <ul className={listClasses}>
-          {selectedElements.length > 0 &&
-            selectedElements.map(element => (
+        <fieldset aria-label={legend}>
+          <ul className={listClasses}>
+            {selectedElements.length > 0 &&
+              selectedElements.map(element => (
+                <MultiSelectOption
+                  active={activeElement === element}
+                  element={element}
+                  key={element}
+                  onHover={this.handleElementHover}
+                  onSelectChange={this.handleSelectChange}
+                  renderLabel={renderLabel}
+                  selected={true}
+                />
+              ))}
+            {unselectedElements.length > 0 &&
+              unselectedElements.map(element => (
+                <MultiSelectOption
+                  active={activeElement === element}
+                  disabled={!allowSelection}
+                  element={element}
+                  key={element}
+                  onHover={this.handleElementHover}
+                  onSelectChange={this.handleSelectChange}
+                  renderLabel={renderLabel}
+                />
+              ))}
+            {showNewElement && (
               <MultiSelectOption
-                active={activeElement === element}
-                element={element}
-                key={element}
+                active={activeElement === query}
+                custom={true}
+                element={query}
+                key={query}
                 onHover={this.handleElementHover}
                 onSelectChange={this.handleSelectChange}
                 renderLabel={renderLabel}
-                selected={true}
               />
-            ))}
-          {unselectedElements.length > 0 &&
-            unselectedElements.map(element => (
-              <MultiSelectOption
-                active={activeElement === element}
-                disabled={!allowSelection}
-                element={element}
-                key={element}
-                onHover={this.handleElementHover}
-                onSelectChange={this.handleSelectChange}
-                renderLabel={renderLabel}
-              />
-            ))}
-          {showNewElement && (
-            <MultiSelectOption
-              active={activeElement === query}
-              custom={true}
-              element={query}
-              key={query}
-              onHover={this.handleElementHover}
-              onSelectChange={this.handleSelectChange}
-              renderLabel={renderLabel}
-            />
-          )}
-          {!showNewElement && selectedElements.length < 1 && unselectedElements.length < 1 && (
-            <li className="spacer-left">{translateWithParameters('no_results_for_x', query)}</li>
-          )}
-        </ul>
+            )}
+            {!showNewElement && selectedElements.length < 1 && unselectedElements.length < 1 && (
+              <li className="spacer-left">{translateWithParameters('no_results_for_x', query)}</li>
+            )}
+          </ul>
+        </fieldset>
         {footerNode}
       </div>
     );
index a963bdf5449280bc1b99cd7f8646160dc0d81fab..a2e5c946cbdc32aab02549783dc4ec597887cc26 100644 (file)
@@ -55,7 +55,9 @@ it('should render with the focus inside the search input', () => {
    */
   const container = document.createElement('div');
   document.body.appendChild(container);
-  const multiselect = mount(<MultiSelect {...props} />, { attachTo: container });
+  const multiselect = mount(<MultiSelect legend="multi select" {...props} />, {
+    attachTo: container
+  });
 
   expect(multiselect.find('input').getDOMNode()).toBe(document.activeElement);
 
@@ -94,6 +96,7 @@ function shallowRender(overrides: Partial<MultiSelectProps> = {}) {
     <MultiSelect
       selectedElements={['bar']}
       elements={[]}
+      legend="multi select"
       onSearch={() => Promise.resolve()}
       onSelect={jest.fn()}
       onUnselect={jest.fn()}
index bd2ca89e1dc834d8638271149998658f9dab4d9d..2ec8d126534155b5b3c121eb914859c936eaa0da 100644 (file)
@@ -16,19 +16,23 @@ exports[`should render multiselect with selected elements 1`] = `
       value=""
     />
   </div>
-  <ul
-    className="menu menu-vertically-limited spacer-top with-top-separator"
+  <fieldset
+    aria-label="multi select"
   >
-    <MultiSelectOption
-      active={true}
-      element="bar"
-      key="bar"
-      onHover={[Function]}
-      onSelectChange={[Function]}
-      renderLabel={[Function]}
-      selected={true}
-    />
-  </ul>
+    <ul
+      className="menu menu-vertically-limited spacer-top with-top-separator"
+    >
+      <MultiSelectOption
+        active={true}
+        element="bar"
+        key="bar"
+        onHover={[Function]}
+        onSelectChange={[Function]}
+        renderLabel={[Function]}
+        selected={true}
+      />
+    </ul>
+  </fieldset>
 </div>
 `;
 
@@ -48,37 +52,41 @@ exports[`should render multiselect with selected elements 2`] = `
       value=""
     />
   </div>
-  <ul
-    className="menu menu-vertically-limited spacer-top with-top-separator"
+  <fieldset
+    aria-label="multi select"
   >
-    <MultiSelectOption
-      active={true}
-      element="bar"
-      key="bar"
-      onHover={[Function]}
-      onSelectChange={[Function]}
-      renderLabel={[Function]}
-      selected={true}
-    />
-    <MultiSelectOption
-      active={false}
-      disabled={false}
-      element="foo"
-      key="foo"
-      onHover={[Function]}
-      onSelectChange={[Function]}
-      renderLabel={[Function]}
-    />
-    <MultiSelectOption
-      active={false}
-      disabled={false}
-      element="baz"
-      key="baz"
-      onHover={[Function]}
-      onSelectChange={[Function]}
-      renderLabel={[Function]}
-    />
-  </ul>
+    <ul
+      className="menu menu-vertically-limited spacer-top with-top-separator"
+    >
+      <MultiSelectOption
+        active={true}
+        element="bar"
+        key="bar"
+        onHover={[Function]}
+        onSelectChange={[Function]}
+        renderLabel={[Function]}
+        selected={true}
+      />
+      <MultiSelectOption
+        active={false}
+        disabled={false}
+        element="foo"
+        key="foo"
+        onHover={[Function]}
+        onSelectChange={[Function]}
+        renderLabel={[Function]}
+      />
+      <MultiSelectOption
+        active={false}
+        disabled={false}
+        element="baz"
+        key="baz"
+        onHover={[Function]}
+        onSelectChange={[Function]}
+        renderLabel={[Function]}
+      />
+    </ul>
+  </fieldset>
 </div>
 `;
 
@@ -98,37 +106,41 @@ exports[`should render multiselect with selected elements 3`] = `
       value=""
     />
   </div>
-  <ul
-    className="menu menu-vertically-limited spacer-top with-top-separator"
+  <fieldset
+    aria-label="multi select"
   >
-    <MultiSelectOption
-      active={false}
-      element="bar"
-      key="bar"
-      onHover={[Function]}
-      onSelectChange={[Function]}
-      renderLabel={[Function]}
-      selected={true}
-    />
-    <MultiSelectOption
-      active={false}
-      disabled={false}
-      element="foo"
-      key="foo"
-      onHover={[Function]}
-      onSelectChange={[Function]}
-      renderLabel={[Function]}
-    />
-    <MultiSelectOption
-      active={true}
-      disabled={false}
-      element="baz"
-      key="baz"
-      onHover={[Function]}
-      onSelectChange={[Function]}
-      renderLabel={[Function]}
-    />
-  </ul>
+    <ul
+      className="menu menu-vertically-limited spacer-top with-top-separator"
+    >
+      <MultiSelectOption
+        active={false}
+        element="bar"
+        key="bar"
+        onHover={[Function]}
+        onSelectChange={[Function]}
+        renderLabel={[Function]}
+        selected={true}
+      />
+      <MultiSelectOption
+        active={false}
+        disabled={false}
+        element="foo"
+        key="foo"
+        onHover={[Function]}
+        onSelectChange={[Function]}
+        renderLabel={[Function]}
+      />
+      <MultiSelectOption
+        active={true}
+        disabled={false}
+        element="baz"
+        key="baz"
+        onHover={[Function]}
+        onSelectChange={[Function]}
+        renderLabel={[Function]}
+      />
+    </ul>
+  </fieldset>
 </div>
 `;
 
@@ -148,45 +160,49 @@ exports[`should render multiselect with selected elements 4`] = `
       value="test"
     />
   </div>
-  <ul
-    className="menu menu-vertically-limited spacer-top with-top-separator"
+  <fieldset
+    aria-label="multi select"
   >
-    <MultiSelectOption
-      active={false}
-      element="bar"
-      key="bar"
-      onHover={[Function]}
-      onSelectChange={[Function]}
-      renderLabel={[Function]}
-      selected={true}
-    />
-    <MultiSelectOption
-      active={false}
-      disabled={false}
-      element="foo"
-      key="foo"
-      onHover={[Function]}
-      onSelectChange={[Function]}
-      renderLabel={[Function]}
-    />
-    <MultiSelectOption
-      active={true}
-      disabled={false}
-      element="baz"
-      key="baz"
-      onHover={[Function]}
-      onSelectChange={[Function]}
-      renderLabel={[Function]}
-    />
-    <MultiSelectOption
-      active={false}
-      custom={true}
-      element="test"
-      key="test"
-      onHover={[Function]}
-      onSelectChange={[Function]}
-      renderLabel={[Function]}
-    />
-  </ul>
+    <ul
+      className="menu menu-vertically-limited spacer-top with-top-separator"
+    >
+      <MultiSelectOption
+        active={false}
+        element="bar"
+        key="bar"
+        onHover={[Function]}
+        onSelectChange={[Function]}
+        renderLabel={[Function]}
+        selected={true}
+      />
+      <MultiSelectOption
+        active={false}
+        disabled={false}
+        element="foo"
+        key="foo"
+        onHover={[Function]}
+        onSelectChange={[Function]}
+        renderLabel={[Function]}
+      />
+      <MultiSelectOption
+        active={true}
+        disabled={false}
+        element="baz"
+        key="baz"
+        onHover={[Function]}
+        onSelectChange={[Function]}
+        renderLabel={[Function]}
+      />
+      <MultiSelectOption
+        active={false}
+        custom={true}
+        element="test"
+        key="test"
+        onHover={[Function]}
+        onSelectChange={[Function]}
+        renderLabel={[Function]}
+      />
+    </ul>
+  </fieldset>
 </div>
 `;
index c93aa947e5bf86843db7acafe7e0bc34918b9668..06129867761d5adbe4690fbd6d903df47abc2315 100644 (file)
@@ -36,6 +36,7 @@ export default function TagsSelector(props: Props) {
     <MultiSelect
       elements={props.tags}
       listSize={props.listSize}
+      legend={translate('select_tags')}
       onSearch={props.onSearch}
       onSelect={props.onSelect}
       onUnselect={props.onUnselect}
index d3a553d60a814441eb9a5f25c9be7d428d42bb57..635be5982c27326409d8d4938e470df2e26c2d47 100644 (file)
@@ -10,6 +10,7 @@ exports[`should render with selected tags 1`] = `
     ]
   }
   filterSelected={[Function]}
+  legend="select_tags"
   listSize={10}
   onSearch={[Function]}
   onSelect={[Function]}
@@ -29,6 +30,7 @@ exports[`should render without tags at all 1`] = `
 <MultiSelect
   elements={Array []}
   filterSelected={[Function]}
+  legend="select_tags"
   listSize={10}
   onSearch={[Function]}
   onSelect={[Function]}
index f11fd2324484b3bc6431b4266a63ac0cb4046f2c..eb7a42fcb26cc312c309cb53d38bf497c2344edc 100644 (file)
@@ -181,6 +181,7 @@ see_all=See all
 see_x=See {0}
 select_verb=Select
 selected=Selected
+select_tags=Add or remove tags
 set=Set
 set_up=Set Up
 severity=Severity
@@ -1521,6 +1522,7 @@ project_activity.graphs.duplications=Duplications
 project_activity.graphs.custom=Custom
 project_activity.graphs.custom.add=Add metric
 project_activity.graphs.custom.add_metric=Add a metric
+project_activity.graphs.custom.select_metric=Select metric to display
 project_activity.graphs.custom.add_metric_info=Only 3 metrics of the same type can be displayed on one graph. You can have a maximum of two graphs.
 project_activity.graphs.custom.no_history=There isn't enough data to generate an activity graph, please select more metrics.
 project_activity.graphs.custom.metric_no_history=This metric has no historical data to display.