]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11773 Make issues 'See Rules Details' accessible
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Thu, 6 Jun 2019 09:09:45 +0000 (11:09 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 28 Jun 2019 06:45:38 +0000 (08:45 +0200)
server/sonar-web/src/main/js/components/workspace/WorkspaceRuleViewer.tsx
server/sonar-web/src/main/js/components/workspace/__tests__/WorkspaceRuleViewer-test.tsx
server/sonar-web/src/main/js/components/workspace/__tests__/__snapshots__/WorkspaceRuleViewer-test.tsx.snap

index c30aa9279d86a01acb962608c3b930dd2e92fbf7..e418177f9654eb1eec804a3fcd19940e5b47dabd 100644 (file)
@@ -30,17 +30,34 @@ export interface Props extends T.Omit<WorkspaceHeaderProps, 'children' | 'onClos
   onLoad: (details: { key: string; name: string }) => void;
 }
 
+interface State {
+  loading: boolean;
+}
+
 export default class WorkspaceRuleViewer extends React.PureComponent<Props> {
+  state: State = { loading: true };
+
+  componentDidUpdate(prevProps: Props) {
+    if (prevProps.rule.key !== this.props.rule.key) {
+      this.setState({ loading: true });
+    }
+  }
+
   handleClose = () => {
     this.props.onClose(this.props.rule.key);
   };
 
   handleLoaded = (rule: { name: string }) => {
     this.props.onLoad({ key: this.props.rule.key, name: rule.name });
+    // Allow time for the actual rendering, and the browser to pick it up.
+    setTimeout(() => {
+      this.setState({ loading: false });
+    }, 1000);
   };
 
   render() {
     const { rule } = this.props;
+    const { loading } = this.state;
 
     return (
       <div className="workspace-viewer">
@@ -54,7 +71,11 @@ export default class WorkspaceRuleViewer extends React.PureComponent<Props> {
           <WorkspaceRuleTitle rule={rule} />
         </WorkspaceHeader>
 
-        <div className="workspace-viewer-container" style={{ height: this.props.height }}>
+        <div
+          aria-busy={loading}
+          aria-live="polite"
+          className="workspace-viewer-container"
+          style={{ height: this.props.height }}>
           <WorkspaceRuleDetails
             onLoad={this.handleLoaded}
             organizationKey={rule.organization}
index 9092b38f1ecc4c810a76424d9fcebd643bab3a3f..62d39d710e46489ebe5913ebd9955332382a8a5e 100644 (file)
@@ -21,10 +21,35 @@ import * as React from 'react';
 import { shallow } from 'enzyme';
 import WorkspaceRuleViewer, { Props } from '../WorkspaceRuleViewer';
 
+jest.useFakeTimers();
+
 it('should render', () => {
   expect(shallowRender()).toMatchSnapshot();
 });
 
+it('should correctly mark the content as busy loading (aria)', () => {
+  const rule = { key: 'foo', name: 'Foo', organization: 'org' };
+  const wrapper = shallowRender({ rule });
+  const instance = wrapper.instance();
+  const container = () => wrapper.find('.workspace-viewer-container');
+
+  expect(container().prop('aria-busy')).toBe(true);
+
+  instance.handleLoaded(rule);
+  jest.runAllTimers();
+  wrapper.update();
+  expect(container().prop('aria-busy')).toBe(false);
+
+  const newRule = { key: 'bar', name: 'Bar', organization: 'org' };
+  wrapper.setProps({ rule: newRule }).update();
+  expect(container().prop('aria-busy')).toBe(true);
+
+  instance.handleLoaded(newRule);
+  jest.runAllTimers();
+  wrapper.update();
+  expect(container().prop('aria-busy')).toBe(false);
+});
+
 it('should close', () => {
   const onClose = jest.fn();
   const wrapper = shallowRender({ onClose });
@@ -41,8 +66,8 @@ it('should call back after load', () => {
 });
 
 function shallowRender(props?: Partial<Props>) {
-  const rule = { key: 'foo', organization: 'org' };
-  return shallow(
+  const rule = { key: 'foo', name: 'Foo', organization: 'org' };
+  return shallow<WorkspaceRuleViewer>(
     <WorkspaceRuleViewer
       height={300}
       onClose={jest.fn()}
index 9af15132081e52481d9babd3d30f8a476fcc48b3..f2515a8cfcaa2c6ff451c3f1618722cd2b30a1ab 100644 (file)
@@ -15,12 +15,15 @@ exports[`should render 1`] = `
       rule={
         Object {
           "key": "foo",
+          "name": "Foo",
           "organization": "org",
         }
       }
     />
   </WorkspaceHeader>
   <div
+    aria-busy={true}
+    aria-live="polite"
     className="workspace-viewer-container"
     style={
       Object {