From 15a87c164fac4053568777c45be1799f4f7b1804 Mon Sep 17 00:00:00 2001 From: v-rakeshsh <155614445+v-rakeshsh@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:10:10 +0530 Subject: [PATCH] feat(fluentUI-migration): ActionButton fluentui v8 to v9 migration (#7401) #### Details Action Button migration from V8 to V9 | V8 Component | V9 Component | Reference Link | |--------|--------|--------| | Action Button | Button | https://react.fluentui.dev/?path=/docs/components-button-button--docs | | Primary Button | Button | https://react.fluentui.dev/?path=/docs/components-button-button--docs | | CommandBarButton | Combination of Menu, MenuTrigger, MenuButton, MenuPopover, MenuList | 1. MenuButton - https://react.fluentui.dev/?path=/docs/components-button-menubutton--docs 2. Menu - https://react.fluentui.dev/?path=/docs/components-menu-menu--docs 3. MenuList - https://react.fluentui.dev/?path=/docs/components-menu-menulist--docs | | IconProps for Action Button and action button internally takes care of showing the icon | Created a component **src/common/icons/fluentui-v9-icons.tsx** to consume @fluentui/react-icons icons | Cell | ### Styling Improvements: * Updated the width property from `100%` to `auto` for better layout flexibility in multiple snapshot files. * Added new CSS classes for better button and icon styling, including hover and focus states. * Adjusted font-weight to `normal` for better text consistency. ### Component Enhancements: * Added `FluentUIV9Icon` import and updated the components to use the new icon. * Improved the components with new styles and updated its component structure to use Fluent UI v9 components. ##### Motivation 1. Migrate Action Button of V8 to V9 Button in all locations of code. 2. Changed types to align with V9 button. 3. Changed styling to align with old UI. 4. Fixed test cases to cover >90% coverage. ##### Context UI locations for all the files are mentioned with images in below file: [ActionButtonComparison](https://microsoft-my.sharepoint.com/:w:/r/personal/v-singhanjal_microsoft_com/_layouts/15/Doc.aspx?sourcedoc=%7B8893E880-1CFB-4D25-8E5F-3AD94CA0E9EB%7D&file=Document%202.docx&action=editNew&mobileredirect=true&wdOrigin=MARKETING.WORD.SIGNIN%2CAPPHOME-WEB.BANNER.NEWBLANK&wdPreviousSession=2dab2adc-a3f9-4816-a2b9-7175f764cb0f&wdPreviousSessionSrc=AppHomeWeb&ct=1724306297464&share=IQGA6JOI-xwlTY5fOtlMoOnrAX4RP9xcmRvEolaQPAyCsOs) Due to FluentUI V9 Migration there are slight differences between old and new Icons. PFA screenshots below. - **More Actions, Collapse all Icons** ![image](https://github.com/user-attachments/assets/db29f19c-b089-4f7f-996c-ccac195ee0a6) ![image](https://github.com/user-attachments/assets/dcf1041f-a1b1-4a81-b88a-65643fc805c4) - **Expand, Move to Assessment Icons** ![image](https://github.com/user-attachments/assets/1637ab2c-874f-47a1-a702-f71f82a9d920) ![image](https://github.com/user-attachments/assets/69e55a22-b464-441d-b22d-e6ca617ed6fe) - **Export result, Save Assessment, Load Assessment, Start over Icons** ![image](https://github.com/user-attachments/assets/c34995c5-4b1d-46bc-8bb8-b4631f7065c2) ![image](https://github.com/user-attachments/assets/331cf997-531b-475a-a2ac-31551d50e60b) - **File issue, Copy failure details Icons** ![image](https://github.com/user-attachments/assets/ddc57a1c-e8cd-4c25-b798-a948c80f61ed) ![image](https://github.com/user-attachments/assets/aacfd3df-dc58-4df5-8380-76a41cad190e) Note : 1. isNarrowMode property is added in details-view-command-bar.tsx and other related files to differentiate the rendering of button in normal mode and menu in minimize mode. 2. SaveAssessmentDialog is now moved out of SaveAssessmentButton file as with v9 button migration to menu, dialog in narrow mode was not rendering as it was getting close once menu button is clicked. 3. Accessibility test has a failure for aria-hidden-focus. There is an existing issue with fluentui and as per them these are Tabster dummy inputs and they must be added to the exception list. They are intentionally aria - hidden as their purpose is to redirect focus to the correct element right when they receive focus and not having aria - hidden will result in the screen readers choking starting to announce them. Please refer for more information on the error:https://github.com/microsoft/fluentui/issues/25133 5. Changed buttonRef: IRefObject; to ButtonRefFunction, as ButtonRefFunction can be used in future when we migrate the dialog to v9 from V8. 6. Changed /load-assessment-button.tsx to functional component from class component so that i can consume makeStyles hooks, as hooks cannot be used in class components, hence converted to Functional component. 7. Removed IPoint because its deprecated in latest Fluent UI V8. 8. Removed expand-collapse-all-button.scss because to consume makeStyles hooks and to use V9 themes, hence created expand-collapse-all-button.-styles.tsx. Technical Debt: These will be take care in up coming PR for v8 to v9 migration. **IButton**: 1. We are stilling using IButton from v8 for ref object in src\DetailsView\components\details-view-command-bar.tsx, because when we tried to use useRef instead of IButton, we have converted the component from class to functional component to consume useRef hook. 2. But after converting to functional component and using useRef, focus back to kebab button is not going in narrow mode when we close the dialog. **Card footer button scroll behaviour** 1. If multiple issues are shown in the card footer, when we expand all issues, and if we click on the first issue section button, scroll goes to the bottom of the page i.e, the focus goes to the last section. **Closing dialog focus issue** 1. In Narromode,When we open the dialog on click of export or save assessment or start over, and when we close the dialog, focus is not going back to the Kebab button, as dialog is still in v8, we will take care of this focus back issues during v9 dialog migration. **Focus style inconsistency** 1. We explicitly added focus styling for buttons to show focus like kebab button / buttons, after closing the file issue dialog, but when we use keyboard to navigate to these kebab or other buttons, browser default focus styles are getting applied, for which it might look thick border for keyboard focus, but on close of dialog, the focus goes to button with little thin border. #### Pull request checklist - [ ] Addresses an existing issue: #0000 - [x] Ran `yarn fastpass` - [x] Added/updated relevant unit test(s) (and ran `yarn test`) - [x] Verified code coverage for the changes made. Check coverage report at: `/test-results/unit/coverage` - [ ] PR title *AND* final merge commit title both start with a semantic tag (`fix:`, `chore:`, `feat(feature-name):`, `refactor:`). See `CONTRIBUTING.md`. - [x] (UI changes only) Added screenshots/GIFs to description above - [x] (UI changes only) Verified usability with NVDA/JAWS --------- Co-authored-by: v-sharmachir Co-authored-by: Prachi Naigaonkar Co-authored-by: Chirag Sharma <150002431+v-sharmachir@users.noreply.github.com> Co-authored-by: Jeevani Chinthala Co-authored-by: Anjali Singh --- .../axe-results-with-issues.snap.html | 31 +- .../axe-results-without-issues.snap.html | 31 +- ...sults-with-baseline-aware-issues.snap.html | 31 +- .../combined-results-with-issues.snap.html | 31 +- .../combined-results-without-issues.snap.html | 31 +- .../summary-scan-with-issues.snap.html | 31 +- .../summary-scan-without-issues.snap.html | 31 +- .../components/assessment-instance-table.tsx | 5 +- .../components/command-bar-buttons-menu.scss | 52 +- .../components/command-bar-buttons-menu.tsx | 71 +- .../components/command-button-styles.tsx | 27 + .../components/common-dialog-styles.scss | 3 +- .../components/details-view-command-bar.tsx | 33 +- .../failure-instance-panel-control.tsx | 21 +- src/DetailsView/components/iframe-warning.tsx | 6 +- .../components/inline-start-over-button.tsx | 11 +- .../components/load-assessment-button.tsx | 27 +- .../components/report-export-button.tsx | 18 +- .../components/requirement-instructions.scss | 1 + ...nt-view-next-requirement-configuration.tsx | 11 +- .../components/requirement-view.scss | 4 +- .../save-assessment-button-factory.tsx | 2 + .../components/save-assessment-button.tsx | 84 +- .../components/save-assessment-dialog.tsx | 76 + .../start-over-component-factory.tsx | 44 +- .../components/start-over-dropdown-styles.tsx | 57 + .../components/start-over-dropdown.tsx | 157 +- .../components/start-over-menu-item.scss | 15 + .../components/static-content-common.scss | 5 +- ...b-stops-instance-section-props-factory.tsx | 2 +- .../transfer-to-assessment-button.tsx | 18 +- .../card-footer-instance-action-buttons.scss | 42 +- .../card-footer-instance-action-buttons.tsx | 201 +- .../cards/card-footer-menu-items-builder.ts | 21 +- .../cards/collapsible-component-cards.scss | 5 +- .../cards/collapsible-component-cards.tsx | 12 +- .../expand-collapse-all-button-styles.tsx | 19 + .../cards/expand-collapse-all-button.scss | 9 - .../cards/expand-collapse-all-button.tsx | 39 +- .../cards/rules-with-instances.scss | 1 + .../components/cards/rules-with-instances.tsx | 2 +- .../components/collapsible-component.scss | 7 +- .../components/collapsible-component.tsx | 19 +- .../insights-command-button-style.tsx | 14 + .../controls/insights-command-button.scss | 4 + .../controls/insights-command-button.tsx | 52 +- src/common/components/theme.tsx | 5 +- src/common/icons/fluentui-v9-icons.tsx | 74 + src/common/styles/theme-v9-dark-theme.ts | 3 +- src/popup/Styles/popup.scss | 5 +- src/reports/automated-checks-report.scss | 2 +- src/reports/components/instance-details.scss | 3 +- .../components/outcome-summary-bar.scss | 18 +- .../collapsible-result-section.tsx | 2 +- .../combined-report-rules-only-sections.tsx | 2 +- .../report-collapsible-container.tsx | 2 +- .../common/pretty-print-axe-violations.ts | 3 + .../__snapshots__/reflow-ui.test.ts.snap | 270 ++- .../tests/details-view/reflow-ui.test.ts | 6 + .../assessment-instance-table.test.tsx.snap | 8 +- .../command-bar-buttons-menu.test.tsx.snap | 135 +- .../details-view-command-bar.test.tsx.snap | 1709 +++++++++++++---- ...ilure-instance-panel-control.test.tsx.snap | 143 +- .../iframe-warning.test.tsx.snap | 3 + .../inline-start-over-button.test.tsx.snap | 15 +- .../load-assessment-button.test.tsx.snap | 14 +- .../report-export-button.test.tsx.snap | 15 +- ...xt-requirement-configuration.test.tsx.snap | 9 +- .../save-assessment-button.test.tsx.snap | 52 +- .../save-assessment-dialog.test.tsx.snap | 47 + ...start-over-component-factory.test.tsx.snap | 133 +- .../start-over-dropdown.test.tsx.snap | 143 +- ...ransfer-to-assessment-button.test.tsx.snap | 12 +- .../command-bar-buttons-menu.test.tsx | 79 +- .../details-view-command-bar.test.tsx | 96 +- ...-assessment-dialog-settings.test.tsx.snap} | 0 ... save-assessment-dialog-settings.test.tsx} | 0 .../failure-instance-panel-control.test.tsx | 57 +- .../components/report-export-button.test.tsx | 5 +- .../save-assessment-button.test.tsx | 135 +- .../save-assessment-dialog.test.tsx | 112 ++ .../start-over-component-factory.test.tsx | 14 +- .../components/start-over-dropdown.test.tsx | 174 +- .../collapsible-component.test.tsx.snap | 140 +- ...oter-instance-action-buttons.test.tsx.snap | 272 +-- ...ard-footer-menu-items-builder.test.ts.snap | 16 +- .../collapsible-component-cards.test.tsx.snap | 62 +- .../expand-collapse-all-button.test.tsx.snap | 18 +- .../instance-details-footer.test.tsx.snap | 18 +- ...rd-footer-instance-action-buttons.test.tsx | 69 +- .../card-footer-menu-items-builder.test.ts | 2 +- .../collapsible-component-cards.test.tsx | 21 +- .../cards/expand-collapse-all-button.test.tsx | 17 +- .../components/collapsible-component.test.tsx | 26 +- .../insights-command-button.test.tsx.snap | 109 +- .../controls/insights-command-button.test.tsx | 58 +- .../theme-family-customizer.test.tsx | 57 +- .../report-collapsible-container.test.tsx | 2 +- .../content-panel-button.test.tsx.snap | 67 +- .../content/content-panel-button.test.tsx | 43 +- src/views/content/content-panel-button.tsx | 16 +- src/views/content/content.scss | 19 +- 102 files changed, 4084 insertions(+), 1867 deletions(-) create mode 100644 src/DetailsView/components/command-button-styles.tsx create mode 100644 src/DetailsView/components/save-assessment-dialog.tsx create mode 100644 src/DetailsView/components/start-over-dropdown-styles.tsx create mode 100644 src/common/components/cards/expand-collapse-all-button-styles.tsx delete mode 100644 src/common/components/cards/expand-collapse-all-button.scss create mode 100644 src/common/components/controls/insights-command-button-style.tsx create mode 100644 src/common/icons/fluentui-v9-icons.tsx create mode 100644 src/tests/unit/tests/DetailsView/components/__snapshots__/save-assessment-dialog.test.tsx.snap rename src/tests/unit/tests/DetailsView/components/details-view-overlay/settings-panel/settings/save-assessment-dialog/__snapshots__/{save-assessment-dialog.test.tsx.snap => save-assessment-dialog-settings.test.tsx.snap} (100%) rename src/tests/unit/tests/DetailsView/components/details-view-overlay/settings-panel/settings/save-assessment-dialog/{save-assessment-dialog.test.tsx => save-assessment-dialog-settings.test.tsx} (100%) create mode 100644 src/tests/unit/tests/DetailsView/components/save-assessment-dialog.test.tsx diff --git a/packages/report-e2e-tests/src/examples/axe-results-with-issues.snap.html b/packages/report-e2e-tests/src/examples/axe-results-with-issues.snap.html index 4f4bbb57fc3..f1b5f8f62ea 100644 --- a/packages/report-e2e-tests/src/examples/axe-results-with-issues.snap.html +++ b/packages/report-e2e-tests/src/examples/axe-results-with-issues.snap.html @@ -823,7 +823,7 @@ border: none; display: flex; align-items: baseline; - width: 100%; + width: auto; } .collapsible-container .collapsible-control:hover { background-color: var(--neutral-alpha-4); @@ -993,13 +993,27 @@ color: buttontext !important; } } -.kebab-menu-icon--EUIj-:hover { - border-style: solid !important; - border-width: 1px; - padding-left: 8px !important; - padding-right: 8px !important; - padding-top: 5px !important; - padding-bottom: 5px !important; +.fileissue--x5mek { + font-weight: 400 !important; +} +.fileissue--x5mek:hover > span > svg { + color: var(--insights-button-hover); +} +.fileissue--x5mek:focus { + outline: 1px solid; +} +.copyfailuredetails--NHZE4 { + font-weight: 400 !important; +} +.copyfailuredetails--NHZE4:hover > span > svg { + color: var(--insights-button-hover); +} +.menu-Button--ElHFS:hover { + border: 1px solid var(--primary-text) !important; + color: var(--primary-text) !important; +} +.menu-Button--ElHFS:focus { + outline: 1px solid; } .kebab-menu-button--9Qt0a { margin-right: 8px; @@ -1105,6 +1119,7 @@ display: flex; align-items: baseline; text-align: left; + font-weight: normal; } .rule-details-group--Tb-LW .rule-detail .outcome-chip { vertical-align: middle; diff --git a/packages/report-e2e-tests/src/examples/axe-results-without-issues.snap.html b/packages/report-e2e-tests/src/examples/axe-results-without-issues.snap.html index 6353a8da4ed..82cc6386c5b 100644 --- a/packages/report-e2e-tests/src/examples/axe-results-without-issues.snap.html +++ b/packages/report-e2e-tests/src/examples/axe-results-without-issues.snap.html @@ -823,7 +823,7 @@ border: none; display: flex; align-items: baseline; - width: 100%; + width: auto; } .collapsible-container .collapsible-control:hover { background-color: var(--neutral-alpha-4); @@ -993,13 +993,27 @@ color: buttontext !important; } } -.kebab-menu-icon--EUIj-:hover { - border-style: solid !important; - border-width: 1px; - padding-left: 8px !important; - padding-right: 8px !important; - padding-top: 5px !important; - padding-bottom: 5px !important; +.fileissue--x5mek { + font-weight: 400 !important; +} +.fileissue--x5mek:hover > span > svg { + color: var(--insights-button-hover); +} +.fileissue--x5mek:focus { + outline: 1px solid; +} +.copyfailuredetails--NHZE4 { + font-weight: 400 !important; +} +.copyfailuredetails--NHZE4:hover > span > svg { + color: var(--insights-button-hover); +} +.menu-Button--ElHFS:hover { + border: 1px solid var(--primary-text) !important; + color: var(--primary-text) !important; +} +.menu-Button--ElHFS:focus { + outline: 1px solid; } .kebab-menu-button--9Qt0a { margin-right: 8px; @@ -1105,6 +1119,7 @@ display: flex; align-items: baseline; text-align: left; + font-weight: normal; } .rule-details-group--Tb-LW .rule-detail .outcome-chip { vertical-align: middle; diff --git a/packages/report-e2e-tests/src/examples/combined-results-with-baseline-aware-issues.snap.html b/packages/report-e2e-tests/src/examples/combined-results-with-baseline-aware-issues.snap.html index 6ac41740ecd..8bd749c05c2 100644 --- a/packages/report-e2e-tests/src/examples/combined-results-with-baseline-aware-issues.snap.html +++ b/packages/report-e2e-tests/src/examples/combined-results-with-baseline-aware-issues.snap.html @@ -823,7 +823,7 @@ border: none; display: flex; align-items: baseline; - width: 100%; + width: auto; } .collapsible-container .collapsible-control:hover { background-color: var(--neutral-alpha-4); @@ -993,13 +993,27 @@ color: buttontext !important; } } -.kebab-menu-icon--EUIj-:hover { - border-style: solid !important; - border-width: 1px; - padding-left: 8px !important; - padding-right: 8px !important; - padding-top: 5px !important; - padding-bottom: 5px !important; +.fileissue--x5mek { + font-weight: 400 !important; +} +.fileissue--x5mek:hover > span > svg { + color: var(--insights-button-hover); +} +.fileissue--x5mek:focus { + outline: 1px solid; +} +.copyfailuredetails--NHZE4 { + font-weight: 400 !important; +} +.copyfailuredetails--NHZE4:hover > span > svg { + color: var(--insights-button-hover); +} +.menu-Button--ElHFS:hover { + border: 1px solid var(--primary-text) !important; + color: var(--primary-text) !important; +} +.menu-Button--ElHFS:focus { + outline: 1px solid; } .kebab-menu-button--9Qt0a { margin-right: 8px; @@ -1105,6 +1119,7 @@ display: flex; align-items: baseline; text-align: left; + font-weight: normal; } .rule-details-group--Tb-LW .rule-detail .outcome-chip { vertical-align: middle; diff --git a/packages/report-e2e-tests/src/examples/combined-results-with-issues.snap.html b/packages/report-e2e-tests/src/examples/combined-results-with-issues.snap.html index ee8684840cd..2e231865805 100644 --- a/packages/report-e2e-tests/src/examples/combined-results-with-issues.snap.html +++ b/packages/report-e2e-tests/src/examples/combined-results-with-issues.snap.html @@ -823,7 +823,7 @@ border: none; display: flex; align-items: baseline; - width: 100%; + width: auto; } .collapsible-container .collapsible-control:hover { background-color: var(--neutral-alpha-4); @@ -993,13 +993,27 @@ color: buttontext !important; } } -.kebab-menu-icon--EUIj-:hover { - border-style: solid !important; - border-width: 1px; - padding-left: 8px !important; - padding-right: 8px !important; - padding-top: 5px !important; - padding-bottom: 5px !important; +.fileissue--x5mek { + font-weight: 400 !important; +} +.fileissue--x5mek:hover > span > svg { + color: var(--insights-button-hover); +} +.fileissue--x5mek:focus { + outline: 1px solid; +} +.copyfailuredetails--NHZE4 { + font-weight: 400 !important; +} +.copyfailuredetails--NHZE4:hover > span > svg { + color: var(--insights-button-hover); +} +.menu-Button--ElHFS:hover { + border: 1px solid var(--primary-text) !important; + color: var(--primary-text) !important; +} +.menu-Button--ElHFS:focus { + outline: 1px solid; } .kebab-menu-button--9Qt0a { margin-right: 8px; @@ -1105,6 +1119,7 @@ display: flex; align-items: baseline; text-align: left; + font-weight: normal; } .rule-details-group--Tb-LW .rule-detail .outcome-chip { vertical-align: middle; diff --git a/packages/report-e2e-tests/src/examples/combined-results-without-issues.snap.html b/packages/report-e2e-tests/src/examples/combined-results-without-issues.snap.html index a32457f113f..8f385a00930 100644 --- a/packages/report-e2e-tests/src/examples/combined-results-without-issues.snap.html +++ b/packages/report-e2e-tests/src/examples/combined-results-without-issues.snap.html @@ -823,7 +823,7 @@ border: none; display: flex; align-items: baseline; - width: 100%; + width: auto; } .collapsible-container .collapsible-control:hover { background-color: var(--neutral-alpha-4); @@ -993,13 +993,27 @@ color: buttontext !important; } } -.kebab-menu-icon--EUIj-:hover { - border-style: solid !important; - border-width: 1px; - padding-left: 8px !important; - padding-right: 8px !important; - padding-top: 5px !important; - padding-bottom: 5px !important; +.fileissue--x5mek { + font-weight: 400 !important; +} +.fileissue--x5mek:hover > span > svg { + color: var(--insights-button-hover); +} +.fileissue--x5mek:focus { + outline: 1px solid; +} +.copyfailuredetails--NHZE4 { + font-weight: 400 !important; +} +.copyfailuredetails--NHZE4:hover > span > svg { + color: var(--insights-button-hover); +} +.menu-Button--ElHFS:hover { + border: 1px solid var(--primary-text) !important; + color: var(--primary-text) !important; +} +.menu-Button--ElHFS:focus { + outline: 1px solid; } .kebab-menu-button--9Qt0a { margin-right: 8px; @@ -1105,6 +1119,7 @@ display: flex; align-items: baseline; text-align: left; + font-weight: normal; } .rule-details-group--Tb-LW .rule-detail .outcome-chip { vertical-align: middle; diff --git a/packages/report-e2e-tests/src/examples/summary-scan-with-issues.snap.html b/packages/report-e2e-tests/src/examples/summary-scan-with-issues.snap.html index 48eb7885d27..2cffaae5f5e 100644 --- a/packages/report-e2e-tests/src/examples/summary-scan-with-issues.snap.html +++ b/packages/report-e2e-tests/src/examples/summary-scan-with-issues.snap.html @@ -823,7 +823,7 @@ border: none; display: flex; align-items: baseline; - width: 100%; + width: auto; } .collapsible-container .collapsible-control:hover { background-color: var(--neutral-alpha-4); @@ -993,13 +993,27 @@ color: buttontext !important; } } -.kebab-menu-icon--EUIj-:hover { - border-style: solid !important; - border-width: 1px; - padding-left: 8px !important; - padding-right: 8px !important; - padding-top: 5px !important; - padding-bottom: 5px !important; +.fileissue--x5mek { + font-weight: 400 !important; +} +.fileissue--x5mek:hover > span > svg { + color: var(--insights-button-hover); +} +.fileissue--x5mek:focus { + outline: 1px solid; +} +.copyfailuredetails--NHZE4 { + font-weight: 400 !important; +} +.copyfailuredetails--NHZE4:hover > span > svg { + color: var(--insights-button-hover); +} +.menu-Button--ElHFS:hover { + border: 1px solid var(--primary-text) !important; + color: var(--primary-text) !important; +} +.menu-Button--ElHFS:focus { + outline: 1px solid; } .kebab-menu-button--9Qt0a { margin-right: 8px; @@ -1105,6 +1119,7 @@ display: flex; align-items: baseline; text-align: left; + font-weight: normal; } .rule-details-group--Tb-LW .rule-detail .outcome-chip { vertical-align: middle; diff --git a/packages/report-e2e-tests/src/examples/summary-scan-without-issues.snap.html b/packages/report-e2e-tests/src/examples/summary-scan-without-issues.snap.html index a6da8970e97..5431243a905 100644 --- a/packages/report-e2e-tests/src/examples/summary-scan-without-issues.snap.html +++ b/packages/report-e2e-tests/src/examples/summary-scan-without-issues.snap.html @@ -823,7 +823,7 @@ border: none; display: flex; align-items: baseline; - width: 100%; + width: auto; } .collapsible-container .collapsible-control:hover { background-color: var(--neutral-alpha-4); @@ -993,13 +993,27 @@ color: buttontext !important; } } -.kebab-menu-icon--EUIj-:hover { - border-style: solid !important; - border-width: 1px; - padding-left: 8px !important; - padding-right: 8px !important; - padding-top: 5px !important; - padding-bottom: 5px !important; +.fileissue--x5mek { + font-weight: 400 !important; +} +.fileissue--x5mek:hover > span > svg { + color: var(--insights-button-hover); +} +.fileissue--x5mek:focus { + outline: 1px solid; +} +.copyfailuredetails--NHZE4 { + font-weight: 400 !important; +} +.copyfailuredetails--NHZE4:hover > span > svg { + color: var(--insights-button-hover); +} +.menu-Button--ElHFS:hover { + border: 1px solid var(--primary-text) !important; + color: var(--primary-text) !important; +} +.menu-Button--ElHFS:focus { + outline: 1px solid; } .kebab-menu-button--9Qt0a { margin-right: 8px; @@ -1105,6 +1119,7 @@ display: flex; align-items: baseline; text-align: left; + font-weight: normal; } .rule-details-group--Tb-LW .rule-detail .outcome-chip { vertical-align: middle; diff --git a/src/DetailsView/components/assessment-instance-table.tsx b/src/DetailsView/components/assessment-instance-table.tsx index c3fd59b0136..998e7c28d53 100644 --- a/src/DetailsView/components/assessment-instance-table.tsx +++ b/src/DetailsView/components/assessment-instance-table.tsx @@ -13,6 +13,7 @@ import { IRenderFunction } from '@fluentui/utilities'; import { AssessmentDefaultMessageGenerator } from 'assessments/assessment-default-message-generator'; import { InstanceTableHeaderType, InstanceTableRow } from 'assessments/types/instance-table-data'; import { InsightsCommandButton } from 'common/components/controls/insights-command-button'; +import { FluentUIV9Icon } from 'common/icons/fluentui-v9-icons'; import { ManualTestStatus } from 'common/types/store-data/manual-test-status'; import { hasIn } from 'lodash'; import * as React from 'react'; @@ -125,7 +126,9 @@ export class AssessmentInstanceTable extends React.Component, + }} onClick={this.onPassUnmarkedInstances} disabled={disabled} > diff --git a/src/DetailsView/components/command-bar-buttons-menu.scss b/src/DetailsView/components/command-bar-buttons-menu.scss index 93ca0e64e30..b5183d4665d 100644 --- a/src/DetailsView/components/command-bar-buttons-menu.scss +++ b/src/DetailsView/components/command-bar-buttons-menu.scss @@ -5,16 +5,62 @@ .command-bar-buttons-menu-button { font-size: 16px; + padding: 9px !important; + color: $primary-text !important; + + &:hover { + background-color: $neutral-2 !important; + } + + &:focus { + outline: 1px solid; + } +} + +.menu-list { + top: 19px !important; +} + +.menu-item { + padding-bottom: unset !important; + + &:hover { + background: none !important; + } +} + +.menu-button { + align-items: flex-start !important; + font-weight: 400 !important; + margin-left: -8px !important; + + &:hover { + background: 'none !important'; + color: $insights-button-hover; + + & > span { + & > svg { + color: $insights-button-hover; + } + } + } +} + +.refresh-icon { + color: $insights-button-hover !important; } .command-bar-buttons-submenu { + min-width: fit-content; + button { height: 36px; } - - min-width: fit-content; } .command-bar-buttons-menu { - height: $details-view-command-bar-height; + height: 17px; + margin-left: 9px !important; + margin-top: 8px !important; + margin-bottom: 16px !important; } diff --git a/src/DetailsView/components/command-bar-buttons-menu.tsx b/src/DetailsView/components/command-bar-buttons-menu.tsx index d63904e6627..21d1b682efb 100644 --- a/src/DetailsView/components/command-bar-buttons-menu.tsx +++ b/src/DetailsView/components/command-bar-buttons-menu.tsx @@ -1,13 +1,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. import { - CommandBarButton, - IButton, - IContextualMenuItem, - IRefObject, - TooltipHost, -} from '@fluentui/react'; + Menu, + MenuButton, + MenuList, + MenuPopover, + MenuProps, + MenuTrigger, + Tooltip, +} from '@fluentui/react-components'; +import { FluentUIV9Icon } from 'common/icons/fluentui-v9-icons'; import { NamedFC } from 'common/react/named-fc'; +import { ButtonRefFunction } from 'DetailsView/components/details-view-command-bar'; import { StartOverMenuItem } from 'DetailsView/components/start-over-component-factory'; import * as React from 'react'; import styles from './command-bar-buttons-menu.scss'; @@ -18,30 +22,33 @@ export type CommandBarButtonsMenuProps = { loadAssessmentButton?: JSX.Element | null; transferToAssessmentButton?: JSX.Element | null; getStartOverMenuItem: () => StartOverMenuItem; - buttonRef: IRefObject; + buttonRef?: ButtonRefFunction; + hasSubMenu?: boolean; }; export const CommandBarButtonsMenu = NamedFC( 'CommandBarButtonsMenu', props => { + const [open, setOpen] = React.useState(false); + const onOpenChange: MenuProps['onOpenChange'] = (e, data) => setOpen(data.open); const exportButton = props.renderExportReportButton(); - const overflowItems: IContextualMenuItem[] = []; + const overflowItems: any[] = []; if (exportButton != null) { overflowItems.push({ key: 'export report', - onRender: () =>
{exportButton}
, + children: <>{exportButton}, }); } if (props.saveAssessmentButton && props.loadAssessmentButton) { overflowItems.push( { key: 'save assessment', - onRender: () =>
{props.saveAssessmentButton}
, + children: <>{props.saveAssessmentButton}, }, { key: 'load assessment', - onRender: () =>
{props.loadAssessmentButton}
, + children: <>{props.loadAssessmentButton}, }, ); } @@ -49,7 +56,7 @@ export const CommandBarButtonsMenu = NamedFC( if (props.transferToAssessmentButton) { overflowItems.push({ key: 'transfer to assessment', - onRender: () =>
{props.transferToAssessmentButton}
, + children: <>{props.transferToAssessmentButton}, }); } @@ -59,19 +66,33 @@ export const CommandBarButtonsMenu = NamedFC( }); return ( - - - + <> + + + + } + className={styles.commandBarButtonsMenuButton} + ref={() => props.buttonRef} + /> + + + + + {overflowItems.map((item, index) => ( + {item?.children} + ))} + + + + ); }, ); diff --git a/src/DetailsView/components/command-button-styles.tsx b/src/DetailsView/components/command-button-styles.tsx new file mode 100644 index 00000000000..d5ab409246c --- /dev/null +++ b/src/DetailsView/components/command-button-styles.tsx @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { makeStyles, tokens } from '@fluentui/react-components'; + +export const useCommandButtonStyle = makeStyles({ + assessmentButton: { + fontWeight: '400 !important', + paddingLeft: '5px !important', + textDecoration: 'none !important', + + '&:focus': { + outline: '1px solid', + }, + + '&:hover': { + background: 'none !important', + color: tokens.colorNeutralForeground2BrandHover, + + '& > span': { + '& >svg': { + color: tokens.colorNeutralForeground2BrandHover, + }, + }, + }, + }, +}); diff --git a/src/DetailsView/components/common-dialog-styles.scss b/src/DetailsView/components/common-dialog-styles.scss index 3647cb137f7..ff960381e2e 100644 --- a/src/DetailsView/components/common-dialog-styles.scss +++ b/src/DetailsView/components/common-dialog-styles.scss @@ -3,13 +3,12 @@ div.insights-dialog-main-override { width: 75%; max-width: 500px; + user-select: text; @media screen and (width <= 500px) { min-width: 240px; width: 75%; } - user-select: text; - .dialog-body { font-size: 16px; } diff --git a/src/DetailsView/components/details-view-command-bar.tsx b/src/DetailsView/components/details-view-command-bar.tsx index a73659ca89f..51765cce4fe 100644 --- a/src/DetailsView/components/details-view-command-bar.tsx +++ b/src/DetailsView/components/details-view-command-bar.tsx @@ -35,6 +35,7 @@ import { SaveAssessmentButtonFactoryDeps, SaveAssessmentButtonFactoryProps, } from 'DetailsView/components/save-assessment-button-factory'; +import { SaveAssessmentDialog } from 'DetailsView/components/save-assessment-dialog'; import { ShouldShowReportExportButtonProps } from 'DetailsView/components/should-show-report-export-button'; import { StartOverFactoryDeps } from 'DetailsView/components/start-over-component-factory'; import { @@ -75,6 +76,7 @@ export type DetailsViewCommandBarState = { isInvalidLoadAssessmentDialogOpen: boolean; isLoadAssessmentDialogOpen: boolean; isReportExportDialogOpen: boolean; + isSaveAssessmentDialogOpen: boolean; loadedAssessmentData: VersionedAssessmentData; startOverDialogState: StartOverDialogState; }; @@ -91,6 +93,8 @@ export type TransferToAssessmentButtonFactory = ( props: TransferToAssessmentButtonProps, ) => JSX.Element | null; +export type ButtonRefFunction = (ref: any) => void; + export interface DetailsViewCommandBarProps { deps: DetailsViewCommandBarDeps; tabStoreData: TabStoreData; @@ -116,6 +120,7 @@ export class DetailsViewCommandBar extends React.Component< public exportDialogCloseFocus?: IButton; public startOverDialogCloseFocus?: IButton; public transferToAssessmentDialogCloseFocus?: IButton; + public loadAssessmentDialogFocus?: IButton; public constructor(props) { super(props); @@ -123,6 +128,7 @@ export class DetailsViewCommandBar extends React.Component< isInvalidLoadAssessmentDialogOpen: false, isLoadAssessmentDialogOpen: false, isReportExportDialogOpen: false, + isSaveAssessmentDialogOpen: false, loadedAssessmentData: {} as VersionedAssessmentData, startOverDialogState: 'none', }; @@ -142,6 +148,7 @@ export class DetailsViewCommandBar extends React.Component< {this.renderLoadAssessmentDialog()} {this.renderStartOverDialog()} {this.renderTransferToAssessmentDialog()} + {this.renderSaveAssessmentDialog()} ); } @@ -262,9 +269,20 @@ export class DetailsViewCommandBar extends React.Component< private renderSaveAssessmentButton = (): JSX.Element | null => { return this.props.switcherNavConfiguration.SaveAssessmentButton({ ...this.props, + handleSaveAssessmentButtonClick: this.handleSaveAssessmentButtonClick, }); }; + private renderSaveAssessmentDialog = (): JSX.Element | null => { + return ( + + ); + }; + private renderTransferToAssessmentButton = (): JSX.Element | null => { return this.props.switcherNavConfiguration.TransferToAssessmentButton({ ...this.props, @@ -326,6 +344,19 @@ export class DetailsViewCommandBar extends React.Component< })); }; + private toggleSaveAssessmentDialog = () => { + this.setState(prevState => ({ + isSaveAssessmentDialogOpen: !prevState.isSaveAssessmentDialogOpen, + })); + }; + + private handleSaveAssessmentButtonClick = (event: React.MouseEvent) => { + this.props.deps.getAssessmentActionMessageCreator().saveAssessment(event); + if (this.props.userConfigurationStoreData.showSaveAssessmentDialog) { + this.toggleSaveAssessmentDialog(); + } + }; + private setAssessmentState = (parsedAssessmentData: VersionedAssessmentData) => { this.setState(_ => ({ loadedAssessmentData: parsedAssessmentData, @@ -382,7 +413,7 @@ export class DetailsViewCommandBar extends React.Component< return { ...this.props, openDialog: this.showStartOverDialog, - buttonRef: ref => (this.startOverDialogCloseFocus = ref), + buttonRef: ref => (this.startOverDialogCloseFocus = ref ?? undefined), }; }; diff --git a/src/DetailsView/components/failure-instance-panel-control.tsx b/src/DetailsView/components/failure-instance-panel-control.tsx index f367db179aa..c0f592590f0 100644 --- a/src/DetailsView/components/failure-instance-panel-control.tsx +++ b/src/DetailsView/components/failure-instance-panel-control.tsx @@ -1,10 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { ActionButton, Icon, ILabelStyles, ITextFieldStyles, TextField } from '@fluentui/react'; -import { Link } from '@fluentui/react-components'; +import { Icon, ILabelStyles, ITextFieldStyles, TextField } from '@fluentui/react'; +import { Link, Button } from '@fluentui/react-components'; import { AssessmentsProvider } from 'assessments/types/assessments-provider'; import { FlaggedComponent } from 'common/components/flagged-component'; import { FeatureFlags } from 'common/feature-flags'; +import { FluentUIV9Icon } from 'common/icons/fluentui-v9-icons'; import { CapturedInstanceActionType } from 'common/types/captured-instance-action-type'; import { FailureInstanceData } from 'common/types/failure-instance-data'; import { FeatureFlagStoreData } from 'common/types/store-data/feature-flag-store-data'; @@ -15,7 +16,6 @@ import { ActionAndCancelButtonsComponent } from './action-and-cancel-buttons-com import { FailureInstancePanelDetails } from './failure-instance-panel-details'; import styles from './failure-instance-panel.scss'; import { GenericPanel, GenericPanelProps } from './generic-panel'; - export interface FailureInstancePanelControlProps { step: string; test: VisualizationType; @@ -83,19 +83,20 @@ export class FailureInstancePanelControl extends React.Component< private renderButton(): JSX.Element { if (this.props.actionType === CapturedInstanceActionType.CREATE) { return ( - } onClick={this.openFailureInstancePanel} > {FailureInstancePanelControl.addFailureInstanceLabel} - + ); } else { return ( - + ); } @@ -207,7 +208,7 @@ export class FailureInstancePanelControl extends React.Component< }); }; - private onValidateSelector = (event): void => { + private onValidateSelector = (): void => { this.props.addPathForValidation(this.state.currentInstance.path); }; diff --git a/src/DetailsView/components/iframe-warning.tsx b/src/DetailsView/components/iframe-warning.tsx index 21a7f19eb42..707e64ba013 100644 --- a/src/DetailsView/components/iframe-warning.tsx +++ b/src/DetailsView/components/iframe-warning.tsx @@ -19,7 +19,11 @@ export const IframeWarningContainerAutomationId = 'iframe-warning-container'; export const IframeWarning = NamedFC('IframeWarning', props => (
There are iframes in the target page. To have complete results,{' '} - + give Accessibility Insights additional permissions ; this will trigger a rescan of the test. diff --git a/src/DetailsView/components/inline-start-over-button.tsx b/src/DetailsView/components/inline-start-over-button.tsx index 7e98b66b229..a2438ffb3ce 100644 --- a/src/DetailsView/components/inline-start-over-button.tsx +++ b/src/DetailsView/components/inline-start-over-button.tsx @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. - import { InsightsCommandButton } from 'common/components/controls/insights-command-button'; +import { FluentUIV9Icon } from 'common/icons/fluentui-v9-icons'; import { NamedFC } from 'common/react/named-fc'; import { VisualizationType } from 'common/types/visualization-type'; import { DetailsViewActionMessageCreator } from 'DetailsView/actions/details-view-action-message-creator'; @@ -23,11 +23,14 @@ export const InlineStartOverButton = NamedFC( return ( , + }} className={styles.inlineStartOverButton} data-automation-id={inlineStartOverButtonDataAutomationId} - /> + > + Start over + ); }, ); diff --git a/src/DetailsView/components/load-assessment-button.tsx b/src/DetailsView/components/load-assessment-button.tsx index 4c23fa027fb..37a0d5f5959 100644 --- a/src/DetailsView/components/load-assessment-button.tsx +++ b/src/DetailsView/components/load-assessment-button.tsx @@ -1,12 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. - import { AssessmentDataParser } from 'common/assessment-data-parser'; import { InsightsCommandButton } from 'common/components/controls/insights-command-button'; +import { FluentUIV9Icon } from 'common/icons/fluentui-v9-icons'; +import { NamedFC } from 'common/react/named-fc'; import { AssessmentStoreData } from 'common/types/store-data/assessment-result-data'; import { TabStoreData } from 'common/types/store-data/tab-store-data'; import { UrlParser } from 'common/url-parser'; import { DetailsViewActionMessageCreator } from 'DetailsView/actions/details-view-action-message-creator'; +import { useCommandButtonStyle } from 'DetailsView/components/command-button-styles'; +import { ButtonRefFunction } from 'DetailsView/components/details-view-command-bar'; + import { LoadAssessmentHelper } from 'DetailsView/components/load-assessment-helper'; import * as React from 'react'; @@ -21,20 +25,29 @@ export interface LoadAssessmentButtonProps { tabStoreData: TabStoreData; assessmentStoreData: AssessmentStoreData; handleLoadAssessmentButtonClick: () => void; + isNarrowMode?: boolean; + buttonRef?: ButtonRefFunction; } export const loadAssessmentButtonAutomationId = 'load-assessment-button'; -export class LoadAssessmentButton extends React.Component { - public render(): JSX.Element { +export const LoadAssessmentButton = NamedFC( + 'LoadAssessmentButton', + props => { + const loadButtonStyles = useCommandButtonStyle(); return ( , + }} + onClick={props.handleLoadAssessmentButtonClick} + className={loadButtonStyles.assessmentButton} + ref={props.buttonRef} + {...props} > Load assessment ); - } -} + }, +); diff --git a/src/DetailsView/components/report-export-button.tsx b/src/DetailsView/components/report-export-button.tsx index 2f7e538a71d..9d101596389 100644 --- a/src/DetailsView/components/report-export-button.tsx +++ b/src/DetailsView/components/report-export-button.tsx @@ -1,24 +1,34 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { IButton, IRefObject } from '@fluentui/react'; import { InsightsCommandButton } from 'common/components/controls/insights-command-button'; +import { FluentUIV9Icon } from 'common/icons/fluentui-v9-icons'; import { NamedFC } from 'common/react/named-fc'; +import { useCommandButtonStyle } from 'DetailsView/components/command-button-styles'; +import { ButtonRefFunction } from 'DetailsView/components/details-view-command-bar'; import * as React from 'react'; export interface ReportExportButtonProps { showReportExportDialog: () => void; - buttonRef?: IRefObject; + buttonRef?: ButtonRefFunction; + isNarrowMode?: boolean; } export const reportExportButtonAutomationId = 'report-export-button'; export const ReportExportButton = NamedFC('ReportExportButton', props => { + const exportButtonStyles = useCommandButtonStyle(); + return ( , + }} onClick={props.showReportExportDialog} - componentRef={props.buttonRef} + ref={props.buttonRef} data-automation-id={reportExportButtonAutomationId} + id={reportExportButtonAutomationId} + className={exportButtonStyles.assessmentButton} + {...props} > Export result diff --git a/src/DetailsView/components/requirement-instructions.scss b/src/DetailsView/components/requirement-instructions.scss index 86cba129b64..8569cd4ed9d 100644 --- a/src/DetailsView/components/requirement-instructions.scss +++ b/src/DetailsView/components/requirement-instructions.scss @@ -9,6 +9,7 @@ line-height: 20px; padding-left: 0.5vw; display: block; + color: $neutral-80; } .requirement-instructions { diff --git a/src/DetailsView/components/requirement-view-next-requirement-configuration.tsx b/src/DetailsView/components/requirement-view-next-requirement-configuration.tsx index 72182239a06..9cc4ac93a44 100644 --- a/src/DetailsView/components/requirement-view-next-requirement-configuration.tsx +++ b/src/DetailsView/components/requirement-view-next-requirement-configuration.tsx @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { PrimaryButton } from '@fluentui/react'; +import { Button } from '@fluentui/react-components'; import { AutomatedChecks } from 'assessments/automated-checks/assessment'; import { AssessmentsProvider } from 'assessments/types/assessments-provider'; import { Assessment } from 'assessments/types/iassessment'; @@ -71,14 +71,17 @@ export const getNextRequirementConfigurationForQuickAssess = ( ); if (requirementIndex === props.deps.quickAssessRequirementKeys.length - 1) { return ( - + > + Complete + ); } diff --git a/src/DetailsView/components/requirement-view.scss b/src/DetailsView/components/requirement-view.scss index ee2cedab751..1cffedec0b1 100644 --- a/src/DetailsView/components/requirement-view.scss +++ b/src/DetailsView/components/requirement-view.scss @@ -33,13 +33,13 @@ } .requirement-content { + padding-left: 32px; + @media screen and (width >= 1200px) { display: grid; grid-template-columns: 1fr 308px; } - padding-left: 32px; - .main-content { margin-top: 10px; width: 90%; diff --git a/src/DetailsView/components/save-assessment-button-factory.tsx b/src/DetailsView/components/save-assessment-button-factory.tsx index 9cba012860d..64090972935 100644 --- a/src/DetailsView/components/save-assessment-button-factory.tsx +++ b/src/DetailsView/components/save-assessment-button-factory.tsx @@ -24,6 +24,8 @@ export type SaveAssessmentButtonFactoryProps = { assessmentStoreData: AssessmentStoreData; tabStoreData: TabStoreData; userConfigurationStoreData: UserConfigurationStoreData; + isNarrowMode?: boolean; + handleSaveAssessmentButtonClick: (event: React.MouseEvent) => void; }; export function getSaveButtonForAssessment(props: SaveAssessmentButtonFactoryProps): JSX.Element { diff --git a/src/DetailsView/components/save-assessment-button.tsx b/src/DetailsView/components/save-assessment-button.tsx index c654dea7bb0..0c15a21f88d 100644 --- a/src/DetailsView/components/save-assessment-button.tsx +++ b/src/DetailsView/components/save-assessment-button.tsx @@ -1,13 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { Checkbox, Dialog, DialogFooter, DialogType, PrimaryButton, Stack } from '@fluentui/react'; -import { useBoolean } from '@fluentui/react-hooks'; import { InsightsCommandButton } from 'common/components/controls/insights-command-button'; +import { FluentUIV9Icon } from 'common/icons/fluentui-v9-icons'; import { UserConfigMessageCreator } from 'common/message-creators/user-config-message-creator'; import { NamedFC } from 'common/react/named-fc'; import { UserConfigurationStoreData } from 'common/types/store-data/user-configuration-store'; import { AssessmentActionMessageCreator } from 'DetailsView/actions/assessment-action-message-creator'; -import styles from 'DetailsView/components/common-dialog-styles.scss'; +import { useCommandButtonStyle } from 'DetailsView/components/command-button-styles'; + import * as React from 'react'; export interface SaveAssessmentButtonDeps { @@ -19,75 +19,29 @@ export interface SaveAssessmentButtonProps { href: string; deps: SaveAssessmentButtonDeps; userConfigurationStoreData: UserConfigurationStoreData; + handleSaveAssessmentButtonClick: (event: React.MouseEvent) => void; + isNarrowMode?: boolean; + buttonRef?: React.RefObject; } export const SaveAssessmentButton = NamedFC( 'SaveAssessmentButton', props => { - const [dialogHidden, { setTrue: hideDialog, setFalse: showDialog }] = useBoolean(true); - - function handleSaveAssessmentClick(event: React.MouseEvent) { - props.deps.getAssessmentActionMessageCreator().saveAssessment(event); - if (props.userConfigurationStoreData.showSaveAssessmentDialog) { - showDialog(); - } - } - - function handleDontShowAgainClick(event: React.MouseEvent, checked?: boolean) { - if (checked === undefined) return; - props.deps.userConfigMessageCreator.setSaveAssessmentDialogState(!checked); - } + const saveAssessmentStyles = useCommandButtonStyle(); return ( - <> - - Save assessment - - - + , + }} + ref={props.buttonRef} + {...props} + > + Save assessment + ); }, ); diff --git a/src/DetailsView/components/save-assessment-dialog.tsx b/src/DetailsView/components/save-assessment-dialog.tsx new file mode 100644 index 00000000000..5399718dec2 --- /dev/null +++ b/src/DetailsView/components/save-assessment-dialog.tsx @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { Checkbox, Dialog, DialogFooter, DialogType, PrimaryButton, Stack } from '@fluentui/react'; +import { UserConfigMessageCreator } from 'common/message-creators/user-config-message-creator'; +import { NamedFC } from 'common/react/named-fc'; +import { UserConfigurationStoreData } from 'common/types/store-data/user-configuration-store'; +import { AssessmentActionMessageCreator } from 'DetailsView/actions/assessment-action-message-creator'; +import styles from 'DetailsView/components/common-dialog-styles.scss'; +import * as React from 'react'; + +export type SaveAssessmentDialogDeps = { + getAssessmentActionMessageCreator: () => AssessmentActionMessageCreator; + userConfigMessageCreator: UserConfigMessageCreator; +}; + +export const saveAssessmentDialogLoadButtonAutomationId = 'save-assessment-dialog-load-button'; +export interface SaveAssessmentDialogProps { + deps: SaveAssessmentDialogDeps; + isOpen: boolean; + onClose: () => void; + userConfigurationStoreData: UserConfigurationStoreData; +} + +export const SaveAssessmentDialog = NamedFC( + 'SaveAssessmentDialog', + props => { + function handleDontShowAgainClick(event: React.MouseEvent, checked?: boolean) { + if (checked === undefined) return; + props.deps.userConfigMessageCreator.setSaveAssessmentDialogState(!checked); + } + return ( + <> + + + ); + }, +); diff --git a/src/DetailsView/components/start-over-component-factory.tsx b/src/DetailsView/components/start-over-component-factory.tsx index d610fded63c..2d381389354 100644 --- a/src/DetailsView/components/start-over-component-factory.tsx +++ b/src/DetailsView/components/start-over-component-factory.tsx @@ -1,11 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { IButton, IContextualMenuItem, IRefObject } from '@fluentui/react'; +import { IContextualMenuItem } from '@fluentui/react'; import { AssessmentsProvider } from 'assessments/types/assessments-provider'; import { InsightsCommandButton } from 'common/components/controls/insights-command-button'; +import { FluentUIV9Icon } from 'common/icons/fluentui-v9-icons'; import { AssessmentStoreData } from 'common/types/store-data/assessment-result-data'; import { VisualizationStoreData } from 'common/types/store-data/visualization-store-data'; import { DetailsViewActionMessageCreator } from 'DetailsView/actions/details-view-action-message-creator'; +import { ButtonRefFunction } from 'DetailsView/components/details-view-command-bar'; import { DetailsRightPanelConfiguration } from 'DetailsView/components/details-view-right-panel'; import { StartOverDialogType } from 'DetailsView/components/start-over-dialog'; import { @@ -27,7 +29,9 @@ export type StartOverFactoryProps = { rightPanelConfiguration: DetailsRightPanelConfiguration; visualizationStoreData: VisualizationStoreData; openDialog: (dialogType: StartOverDialogType) => void; - buttonRef: IRefObject; + buttonRef: ButtonRefFunction; + hasSubMenu?: boolean; + isNarrowMode?: boolean; }; export type StartOverMenuItem = Omit; @@ -41,9 +45,7 @@ export const AssessmentStartOverFactory: StartOverComponentFactory = { getStartOverComponent: props => getStartOverComponentForAssessment(props, 'down'), getStartOverMenuItem: props => { return { - onRender: () => ( -
{getStartOverComponentForAssessment(props, 'left')}
- ), + children:
{getStartOverComponentForAssessment(props, 'left')}
, }; }, }; @@ -52,26 +54,41 @@ export const QuickAssessStartOverFactory: StartOverComponentFactory = { getStartOverComponent: props => getStartOverComponentForQuickAssess(props, 'down'), getStartOverMenuItem: props => { return { - onRender: () => ( -
{getStartOverComponentForQuickAssess(props, 'left')}
- ), + children:
{getStartOverComponentForQuickAssess(props, 'left')}
, }; }, }; export const FastpassStartOverFactory: StartOverComponentFactory = { - getStartOverComponent: props => { - return ; + getStartOverComponent: props => getStartOverComponentFastPass(props), + getStartOverMenuItem: props => { + return { + children:
{getStartOverComponentFastPass(props)}
, + }; }, - getStartOverMenuItem: getStartOverPropsForFastPass, }; +export function getStartOverComponentFastPass(props: StartOverFactoryProps): JSX.Element { + const startOverProps = getStartOverPropsForFastPass(props); + return ( + , + }} + {...startOverProps} + > + {startOverProps.text} + + ); +} + export function getStartOverComponentForAssessment( props: StartOverFactoryProps, dropdownDirection: DropdownDirection, ): JSX.Element { const selectedTest = props.assessmentStoreData.assessmentNavState.selectedTestType; const test = props.deps.getProvider().forType(selectedTest); + const startOverProps: StartOverProps = { singleTestSuffix: test!.title, allTestSuffix: 'Assessment', @@ -80,6 +97,8 @@ export function getStartOverComponentForAssessment( buttonRef: props.buttonRef, rightPanelOptions: props.rightPanelConfiguration.startOverContextMenuKeyOptions, switcherStartOverPreferences: { showTest: true }, + hasSubMenu: props.hasSubMenu, + isNarrowMode: props.isNarrowMode, }; return ; @@ -101,6 +120,8 @@ export function getStartOverComponentForQuickAssess( buttonRef: props.buttonRef, rightPanelOptions: props.rightPanelConfiguration.startOverContextMenuKeyOptions, switcherStartOverPreferences: { showTest }, + hasSubMenu: props.hasSubMenu, + isNarrowMode: props.isNarrowMode, }; return ; @@ -119,5 +140,6 @@ export function getStartOverPropsForFastPass(props: StartOverFactoryProps): Star 'data-automation-id': startOverAutomationId, text: 'Start over', className: styles.startOverMenuItem, + isNarrowMode: props.isNarrowMode, }; } diff --git a/src/DetailsView/components/start-over-dropdown-styles.tsx b/src/DetailsView/components/start-over-dropdown-styles.tsx new file mode 100644 index 00000000000..ffcb99cf455 --- /dev/null +++ b/src/DetailsView/components/start-over-dropdown-styles.tsx @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { makeStyles, tokens } from '@fluentui/react-components'; + +export const useStartOverDropdownStyles: any = makeStyles({ + menuButton: { + alignItems: 'flex-start !important', + fontWeight: '400 !important', + color: tokens.colorNeutralForeground2, + paddingLeft: '4px', + + ':focus': { + outline: '1px solid', + }, + + ':hover': { + background: 'none !important', + color: tokens.colorNeutralForeground2BrandHover, + + '& > span': { + '& > svg': { + color: tokens.colorNeutralForeground2BrandHover, + }, + }, + }, + }, + + chevronIcon: { + color: tokens.colorNeutralForeground2, + + ':hover': { + background: 'none !important', + color: tokens.colorNeutralForeground2BrandHover, + }, + }, + + defaultChevron: { + color: `${tokens.colorNeutralStrokeAccessible} !important`, + + ':hover': { + color: `unset !important`, + }, + }, + + menuPopover: { + borderRadius: tokens.borderRadiusNone, + }, + + menuItem: { + borderRadius: tokens.borderRadiusNone, + + ':hover': { + backgroundColor: 'unset !important', + }, + }, +}); diff --git a/src/DetailsView/components/start-over-dropdown.tsx b/src/DetailsView/components/start-over-dropdown.tsx index 438e0931e1f..ab329c243f8 100644 --- a/src/DetailsView/components/start-over-dropdown.tsx +++ b/src/DetailsView/components/start-over-dropdown.tsx @@ -1,32 +1,45 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +import { DirectionalHint } from '@fluentui/react'; + import { - ContextualMenu, - DirectionalHint, - IButton, - IContextualMenuItem, - IRefObject, -} from '@fluentui/react'; -import { IPoint } from '@fluentui/utilities'; -import { InsightsCommandButton } from 'common/components/controls/insights-command-button'; + Menu, + MenuButton, + MenuItem, + MenuItemProps, + MenuList, + MenuPopover, + MenuTrigger, +} from '@fluentui/react-components'; + +import { FluentUIV9Icon } from 'common/icons/fluentui-v9-icons'; +import { NamedFC } from 'common/react/named-fc'; +import { ButtonRefFunction } from 'DetailsView/components/details-view-command-bar'; import { StartOverDialogType } from 'DetailsView/components/start-over-dialog'; +import { useStartOverDropdownStyles } from 'DetailsView/components/start-over-dropdown-styles'; import * as React from 'react'; import { StartOverContextMenuKeyOptions } from './details-view-right-panel'; +export type StartOverDropdownMenuItems = MenuItemProps & { + name: string; +}; + export interface StartOverState { isContextMenuVisible: boolean; - target?: HTMLElement | string | MouseEvent | IPoint | null; + target?: HTMLElement | string | MouseEvent | null; } export interface StartOverProps { singleTestSuffix: string; dropdownDirection: DropdownDirection; openDialog: (dialogType: StartOverDialogType) => void; - buttonRef: IRefObject; + buttonRef?: ButtonRefFunction; allTestSuffix: string; rightPanelOptions: StartOverContextMenuKeyOptions; switcherStartOverPreferences: StartOverContextMenuKeyOptions; + hasSubMenu?: boolean; + isNarrowMode?: boolean; } const dropdownDirections = { @@ -42,70 +55,26 @@ const dropdownDirections = { export type DropdownDirection = keyof typeof dropdownDirections; -export class StartOverDropdown extends React.Component { - constructor(props: StartOverProps) { - super(props); - - this.state = { - isContextMenuVisible: false, - }; - } - - public render(): JSX.Element { - const direction = this.props.dropdownDirection; - return ( -
- - {this.renderContextMenu()} -
- ); - } - - private renderContextMenu(): JSX.Element | null { - if (!this.state.isContextMenuVisible) { - return null; - } - - const direction = this.props.dropdownDirection; - - return ( - this.dismissDropdown()} - target={this.state.target} - items={this.getMenuItems()} - directionalHint={dropdownDirections[direction].directionalHint} - /> - ); - } - - private getMenuItems(): IContextualMenuItem[] { +export const StartOverDropdown = NamedFC('StartOverDropdown', props => { + const stylesValue: any = useStartOverDropdownStyles(); + const direction = props.dropdownDirection; + const getMenuItemsV9 = (): StartOverDropdownMenuItems[] => { const { singleTestSuffix, allTestSuffix, rightPanelOptions, switcherStartOverPreferences: startOverButtonOptionPreferences, - } = this.props; - const items: IContextualMenuItem[] = []; + } = props; + const items: StartOverDropdownMenuItems[] = []; const assessmentKey = { key: 'assessment', name: `Start over ${allTestSuffix}`, - onClick: this.onStartOverAllTestsMenu, + onClick: onStartOverAllTestsMenu, }; const testKey = { key: 'test', name: `Start over ${singleTestSuffix}`, - onClick: this.onStartOverTestMenu, + onClick: onStartOverTestMenu, }; items.push(assessmentKey); @@ -115,21 +84,59 @@ export class StartOverDropdown extends React.Component { - this.props.openDialog('test'); }; - private onStartOverAllTestsMenu = (): void => { - this.props.openDialog('assessment'); + const onStartOverTestMenu = (): void => { + props.openDialog('test'); }; - private openDropdown = (event): void => { - this.setState({ target: event.currentTarget, isContextMenuVisible: true }); + const onStartOverAllTestsMenu = (): void => { + props.openDialog('assessment'); }; - - private dismissDropdown(): void { - this.setState({ target: null, isContextMenuVisible: false }); - } -} + return ( +
+ + + } + aria-label="start over menu" + menuIcon={ + direction === 'left' ? ( + + ) : ( + + ) + } + > + Start over + + + + + {getMenuItemsV9().map(item => ( + + {item.name} + + ))} + + + +
+ ); +}); diff --git a/src/DetailsView/components/start-over-menu-item.scss b/src/DetailsView/components/start-over-menu-item.scss index 98d78dbb984..7a7915242c4 100644 --- a/src/DetailsView/components/start-over-menu-item.scss +++ b/src/DetailsView/components/start-over-menu-item.scss @@ -3,8 +3,23 @@ @import '../../common/styles/colors.scss'; .start-over-menu-item { + font-weight: 400 !important; + padding-left: 3px !important; + + &:hover { + background: none !important; + color: $insights-button-hover !important; + + & > span { + & > svg { + color: $insights-button-hover; + } + } + } + .start-over-menu-item-icon { line-height: 16px; + margin-left: -8px !important; } button:disabled, diff --git a/src/DetailsView/components/static-content-common.scss b/src/DetailsView/components/static-content-common.scss index 6e19200fead..4baec46015d 100644 --- a/src/DetailsView/components/static-content-common.scss +++ b/src/DetailsView/components/static-content-common.scss @@ -8,6 +8,8 @@ padding-right: $fast-pass-right-panel-margin-right; padding-left: $fast-pass-right-panel-margin-left; margin-bottom: auto; + background-color: $neutral-2; + height: 100%; ol { -webkit-padding-start: 16px; // Chrome versions before 87 use non-standard name: https://caniuse.com/mdn-css_properties_padding-inline-start @@ -21,9 +23,6 @@ i { font-size: 16px; } - - background-color: $neutral-2; - height: 100%; } .details-view-toggle { diff --git a/src/DetailsView/components/tab-stops/tab-stops-instance-section-props-factory.tsx b/src/DetailsView/components/tab-stops/tab-stops-instance-section-props-factory.tsx index 52855af3dae..c1400225d36 100644 --- a/src/DetailsView/components/tab-stops/tab-stops-instance-section-props-factory.tsx +++ b/src/DetailsView/components/tab-stops/tab-stops-instance-section-props-factory.tsx @@ -79,7 +79,7 @@ export const FastPassTabStopsInstanceSectionPropsFactory: TabStopsInstanceSectio buttonAriaLabel: buttonAriaLabel, headingLevel, deps: deps, - onExpandToggle: (event: React.MouseEvent) => { + onExpandToggle: (event: React.MouseEvent) => { deps.tabStopRequirementActionMessageCreator!.toggleTabStopRequirementExpand( result.id, event, diff --git a/src/DetailsView/components/transfer-to-assessment-button.tsx b/src/DetailsView/components/transfer-to-assessment-button.tsx index d5901d940ed..95fbb73f66e 100644 --- a/src/DetailsView/components/transfer-to-assessment-button.tsx +++ b/src/DetailsView/components/transfer-to-assessment-button.tsx @@ -1,19 +1,21 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { IRefObject, IButton } from '@fluentui/react'; import { InsightsCommandButton } from 'common/components/controls/insights-command-button'; +import { FluentUIV9Icon } from 'common/icons/fluentui-v9-icons'; import { NamedFC } from 'common/react/named-fc'; +import { useCommandButtonStyle } from 'DetailsView/components/command-button-styles'; +import { ButtonRefFunction } from 'DetailsView/components/details-view-command-bar'; import { DataTransferViewController } from 'DetailsView/data-transfer-view-controller'; import * as React from 'react'; export type TransferToAssessmentButtonDeps = { dataTransferViewController: DataTransferViewController; }; - export interface TransferToAssessmentButtonProps { deps: TransferToAssessmentButtonDeps; - buttonRef?: IRefObject; + buttonRef?: ButtonRefFunction; + isNarrowMode?: boolean; } export const transferToAssessmentButtonAutomationId = 'transfer-to-assessment-button'; @@ -21,14 +23,20 @@ export const transferToAssessmentButtonAutomationId = 'transfer-to-assessment-bu export const TransferToAssessmentButton = NamedFC( 'TransferToAssessmentButton', props => { + const saveAssessmentStyles = useCommandButtonStyle(); + return ( , + }} onClick={ props.deps.dataTransferViewController.showQuickAssessToAssessmentConfirmDialog } - componentRef={props.buttonRef} + ref={props.buttonRef} + isNarrowMode={props.isNarrowMode} > Move to assessment diff --git a/src/common/components/cards/card-footer-instance-action-buttons.scss b/src/common/components/cards/card-footer-instance-action-buttons.scss index 98e7830d437..b41623a07ca 100644 --- a/src/common/components/cards/card-footer-instance-action-buttons.scss +++ b/src/common/components/cards/card-footer-instance-action-buttons.scss @@ -11,14 +11,44 @@ @media screen and (forced-colors: active) { color: buttontext !important; } +} + +.fileissue { + font-weight: 400 !important; &:hover { - border-style: solid !important; - border-width: 1px; - padding-left: 8px !important; - padding-right: 8px !important; - padding-top: 5px !important; - padding-bottom: 5px !important; + & > span { + & > svg { + color: $insights-button-hover; + } + } + } + + &:focus { + outline: 1px solid; + } +} + +.copyfailuredetails { + font-weight: 400 !important; + + &:hover { + & > span { + & > svg { + color: $insights-button-hover; + } + } + } +} + +.menu-Button { + &:hover { + border: 1px solid $primary-text !important; + color: $primary-text !important; + } + + &:focus { + outline: 1px solid; } } diff --git a/src/common/components/cards/card-footer-instance-action-buttons.tsx b/src/common/components/cards/card-footer-instance-action-buttons.tsx index 2b61787c9c1..8aacb1e4a35 100644 --- a/src/common/components/cards/card-footer-instance-action-buttons.tsx +++ b/src/common/components/cards/card-footer-instance-action-buttons.tsx @@ -1,14 +1,24 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { ActionButton, DirectionalHint, IButton } from '@fluentui/react'; import { registerIcons } from '@fluentui/react/lib/Styling'; import { - CardFooterMenuItem, + Button, + Menu, + MenuButton, + MenuItem, + MenuList, + MenuPopover, + MenuTrigger, + tokens, +} from '@fluentui/react-components'; +import { CardFooterMenuItemsBuilder, CardFooterMenuItemsDeps, } from 'common/components/cards/card-footer-menu-items-builder'; import { CardsViewController } from 'common/components/cards/cards-view-controller'; +import { FluentUIV9Icon } from 'common/icons/fluentui-v9-icons'; import { MoreActionsMenuIcon } from 'common/icons/more-actions-menu-icon'; +import { NamedFC } from 'common/react/named-fc'; import { NarrowModeStatus } from 'DetailsView/components/narrow-mode-detector'; import * as React from 'react'; import { CreateIssueDetailsTextData } from '../../types/create-issue-details-text-data'; @@ -38,102 +48,113 @@ export interface CardFooterInstanceActionButtonsProps { narrowModeStatus?: NarrowModeStatus; } -export class CardFooterInstanceActionButtons extends React.Component { - private toastRef: React.RefObject; - private fileIssueButtonRef: IButton | null; - private kebabButtonRef: IButton | null; - constructor(props: CardFooterInstanceActionButtonsProps) { - super(props); - this.toastRef = React.createRef(); - } +export const CardFooterInstanceActionButtons = NamedFC( + 'CardFooterInstanceActionButtonsProps', + props => { + const toastRef = React.useRef(null); + const fileIssueButtonRef: any = React.useRef(null); + const kebabButtonRef: any = React.useRef(null); - public render(): JSX.Element | null { - const menuItems = this.getMenuItems(); - if (menuItems.length === 0) { - return null; - } + const focusButtonAfterDialogClosed = (): void => { + if (props?.narrowModeStatus?.isCardFooterCollapsed) { + kebabButtonRef?.current?.focus(); + } else { + fileIssueButtonRef?.current?.focus(); + } + }; - return ( - // The wrapper has to be a real element, not a <>, because we want the placeholder elements - // the dialog/toast involve to be considered as part of the button for the purposes of layout - // calculation in this component's parent. -
event.stopPropagation()}> - {this.renderButtons()} - {this.renderCopyFailureDetailsToast()} -
- ); - } + const getMenuItems = () => { + return props.deps.cardFooterMenuItemsBuilder.getCardFooterMenuItems({ + ...props, + toastRef: toastRef, + fileIssueButtonRef: ref => (fileIssueButtonRef.current = ref), + onIssueFilingSettingsDialogDismissed: focusButtonAfterDialogClosed, + }); + }; - public renderButtons(): JSX.Element { - if (this.props.narrowModeStatus?.isCardFooterCollapsed) { - return this.renderKebabButton(); - } else { - return this.renderExpandedButtons(); - } - } + const renderCopyFailureDetailsToast = () => { + const { cardInteractionSupport } = props.deps; - public renderKebabButton(): JSX.Element { - return ( - (this.kebabButtonRef = ref)} - ariaLabel={this.props.kebabMenuAriaLabel || 'More actions'} - menuIconProps={{ - iconName: 'MoreActionsMenuIcon', - className: styles.kebabMenuIcon, - }} - menuProps={{ - directionalHint: DirectionalHint.bottomRightEdge, - shouldFocusOnMount: true, - items: this.getMenuItems(), - }} - /> - ); - } + if (!cardInteractionSupport.supportsCopyFailureDetails) { + return null; + } - public renderExpandedButtons(): JSX.Element { - const menuItems = this.getMenuItems(); + return ; + }; - return ( - <> - {menuItems.map(props => ( - - { + return ( + + + (kebabButtonRef.current = ref)} + appearance="transparent" + shape="square" + aria-label={props?.kebabMenuAriaLabel} + icon={} /> - - ))} - - ); - } - - public renderCopyFailureDetailsToast(): JSX.Element | null { - const { cardInteractionSupport } = this.props.deps; + + + + {getMenuItems().map((item: any, index: number) => ( + } + {...item} + > + {item?.text} + + ))} + + + + ); + }; - if (!cardInteractionSupport.supportsCopyFailureDetails) { - return null; - } + const renderExpandedButtons = () => { + const menuItems = getMenuItems(); + return ( + <> + {menuItems.map(menuItem => ( + + ))} + + ); + }; - return ; - } + const renderButtons = () => { + if (props.narrowModeStatus?.isCardFooterCollapsed) { + return renderKebabButton(); + } else { + return renderExpandedButtons(); + } + }; - private getMenuItems(): CardFooterMenuItem[] { - return this.props.deps.cardFooterMenuItemsBuilder.getCardFooterMenuItems({ - ...this.props, - toastRef: this.toastRef, - fileIssueButtonRef: ref => (this.fileIssueButtonRef = ref), - onIssueFilingSettingsDialogDismissed: this.focusButtonAfterDialogClosed, - }); - } + const menuItems = getMenuItems(); + const menuItemsJsx = + menuItems?.length === 0 ? null : ( +
event.stopPropagation()}> + {renderButtons()} + {renderCopyFailureDetailsToast()} +
+ ); - private focusButtonAfterDialogClosed = (): void => { - if (this.props.narrowModeStatus?.isCardFooterCollapsed) { - this.kebabButtonRef?.focus(); - } else { - this.fileIssueButtonRef?.focus(); - } - }; -} + return menuItemsJsx; + }, +); diff --git a/src/common/components/cards/card-footer-menu-items-builder.ts b/src/common/components/cards/card-footer-menu-items-builder.ts index e602a20f446..62471a69d31 100644 --- a/src/common/components/cards/card-footer-menu-items-builder.ts +++ b/src/common/components/cards/card-footer-menu-items-builder.ts @@ -1,13 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { - IButton, - IButtonProps, - IContextualMenuItem, - IContextualMenuRenderItem, - IRefObject, -} from '@fluentui/react'; +import { IButtonProps, IContextualMenuItem } from '@fluentui/react'; import { IssueDetailsTextGenerator } from 'background/issue-details-text-generator'; import { CardInteractionSupport } from 'common/components/cards/card-interaction-support'; import { CardsViewController } from 'common/components/cards/cards-view-controller'; @@ -18,13 +12,14 @@ import { CreateIssueDetailsTextData } from 'common/types/create-issue-details-te import { ToolData } from 'common/types/store-data/unified-data-interface'; import { UserConfigurationStoreData } from 'common/types/store-data/user-configuration-store'; import { DetailsViewActionMessageCreator } from 'DetailsView/actions/details-view-action-message-creator'; +import { ButtonRefFunction } from 'DetailsView/components/details-view-command-bar'; import { IssueFilingServiceProvider } from 'issue-filing/issue-filing-service-provider'; import React from 'react'; export type CardFooterMenuItem = IContextualMenuItem & IButtonProps; export type CardFooterMenuItemsProps = { - fileIssueButtonRef: IRefObject & IRefObject; + fileIssueButtonRef: ButtonRefFunction; toastRef: React.RefObject; issueDetailsData: CreateIssueDetailsTextData; userConfigurationStoreData: UserConfigurationStoreData | null; @@ -54,11 +49,9 @@ export class CardFooterMenuItemsBuilder { items.push({ key: 'fileissue', text: 'File issue', - iconProps: { - iconName: 'ladybugSolid', - }, + iconName: 'BugFilled', onClick: event => this.fileIssue(props, event), - componentRef: props.fileIssueButtonRef, + componentRef: () => props.fileIssueButtonRef, }); } @@ -66,9 +59,7 @@ export class CardFooterMenuItemsBuilder { items.push({ key: 'copyfailuredetails', text: `Copy failure details`, - iconProps: { - iconName: 'copy', - }, + iconName: 'DocumentCopyRegular', onClick: event => void this.copyFailureDetails(props, event), }); } diff --git a/src/common/components/cards/collapsible-component-cards.scss b/src/common/components/cards/collapsible-component-cards.scss index 7a08179bd9e..767121076ca 100644 --- a/src/common/components/cards/collapsible-component-cards.scss +++ b/src/common/components/cards/collapsible-component-cards.scss @@ -36,8 +36,9 @@ border: none; display: flex; align-items: baseline; - width: 100%; + width: auto; height: fit-content; + margin-left: -5px; &:hover { background-color: $neutral-alpha-4; @@ -58,7 +59,7 @@ } .collapsible-container-content { - margin-left: 24px; + margin-left: 27px; &[aria-hidden='true'] { display: none; diff --git a/src/common/components/cards/collapsible-component-cards.tsx b/src/common/components/cards/collapsible-component-cards.tsx index db55a829077..abb81b60c5d 100644 --- a/src/common/components/cards/collapsible-component-cards.tsx +++ b/src/common/components/cards/collapsible-component-cards.tsx @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { ActionButton } from '@fluentui/react'; +import { Button } from '@fluentui/react-components'; import { css } from '@fluentui/utilities'; import { HeadingElementForLevel, HeadingLevel } from 'common/components/heading-element-for-level'; import { NamedFC } from 'common/react/named-fc'; @@ -24,7 +24,7 @@ export interface CollapsibleComponentCardsProps { containerClassName?: string; buttonAriaLabel?: string; deps: CollapsibleComponentCardsDeps; - onExpandToggle: (event: React.MouseEvent) => void; + onExpandToggle: (event: React.MouseEvent) => void; isExpanded?: boolean; } @@ -58,7 +58,7 @@ const CollapsibleComponentCards = NamedFC( collapsedCSSClassName = null; } - const onClick = (event: React.MouseEvent) => { + const onClick = (event: React.MouseEvent) => { if (event.nativeEvent.detail === 0 && deps.setFocusVisibility != null) { // 0 => keyboard event deps.setFocusVisibility(true); @@ -77,15 +77,15 @@ const CollapsibleComponentCards = NamedFC( )} > - {header} - + {contentWrapper}
diff --git a/src/common/components/cards/expand-collapse-all-button-styles.tsx b/src/common/components/cards/expand-collapse-all-button-styles.tsx new file mode 100644 index 00000000000..cccc508b1a3 --- /dev/null +++ b/src/common/components/cards/expand-collapse-all-button-styles.tsx @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +import { makeStyles, tokens } from '@fluentui/react-components'; + +export const useExpandCollapseAllButtonStyles: any = makeStyles({ + expandCollapseAllButton: { + display: 'flex', + padding: 'unset', + paddingRight: '27px!important', + paddingLeft: '1px!important', + marginLeft: '1px!important', + marginTop: '2px!important', + fontWeight: tokens?.fontSizeBase400, + }, + + customStyleIcon: { + color: tokens.colorNeutralForeground2BrandHover, + }, +}); diff --git a/src/common/components/cards/expand-collapse-all-button.scss b/src/common/components/cards/expand-collapse-all-button.scss deleted file mode 100644 index 16378df2f2a..00000000000 --- a/src/common/components/cards/expand-collapse-all-button.scss +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -.expand-collapse-all-button { - display: flex; - padding: unset; - padding-right: 27px; - margin-left: -2px; - margin-top: 2px; -} diff --git a/src/common/components/cards/expand-collapse-all-button.tsx b/src/common/components/cards/expand-collapse-all-button.tsx index be52744e834..218cb8b6bbd 100644 --- a/src/common/components/cards/expand-collapse-all-button.tsx +++ b/src/common/components/cards/expand-collapse-all-button.tsx @@ -1,10 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { ActionButton } from '@fluentui/react'; +import { Button } from '@fluentui/react-components'; +import { useExpandCollapseAllButtonStyles } from 'common/components/cards/expand-collapse-all-button-styles'; +import { FluentUIV9Icon } from 'common/icons/fluentui-v9-icons'; import { CardSelectionMessageCreator } from 'common/message-creators/card-selection-message-creator'; import { NamedFC } from 'common/react/named-fc'; import * as React from 'react'; -import styles from './expand-collapse-all-button.scss'; export type ExpandCollapseAllButtonProps = { allCardsCollapsed: boolean; @@ -14,30 +15,48 @@ export type ExpandCollapseAllButtonProps = { export const ExpandCollapseAllButton = NamedFC( 'ExpandCollapseAllButton', props => { + const getStyles: any = useExpandCollapseAllButtonStyles(); const { allCardsCollapsed, cardSelectionMessageCreator } = props; let expandCollapseAllButtonHandler = cardSelectionMessageCreator.collapseAllRules; let buttonText = 'Collapse all'; - let iconName = 'ChevronDown'; let ariaLabel: string | undefined = undefined; if (allCardsCollapsed) { expandCollapseAllButtonHandler = cardSelectionMessageCreator.expandAllRules; buttonText = 'Expand all'; - iconName = 'ChevronRight'; ariaLabel = 'Expand all rules to show failed instances.'; } + const IconName = () => { + if (allCardsCollapsed) { + return ( + + ); + } + return ( + + ); + }; + return ( - } + size="medium" + appearance="transparent" + className={getStyles?.expandCollapseAllButton} aria-label={ariaLabel} - className={styles.expandCollapseAllButton} + aria-expanded={!allCardsCollapsed} + onClick={expandCollapseAllButtonHandler} > {buttonText} - + ); }, ); diff --git a/src/common/components/cards/rules-with-instances.scss b/src/common/components/cards/rules-with-instances.scss index bacbcc52b6e..8d315a3daf8 100644 --- a/src/common/components/cards/rules-with-instances.scss +++ b/src/common/components/cards/rules-with-instances.scss @@ -13,6 +13,7 @@ display: flex; align-items: baseline; text-align: left; + font-weight: normal; :global(.outcome-chip) { vertical-align: middle; diff --git a/src/common/components/cards/rules-with-instances.tsx b/src/common/components/cards/rules-with-instances.tsx index 20d70033233..1c8d91cb4c1 100644 --- a/src/common/components/cards/rules-with-instances.tsx +++ b/src/common/components/cards/rules-with-instances.tsx @@ -85,7 +85,7 @@ export const RulesWithInstances = NamedFC( buttonAriaLabel: buttonAriaLabel, headingLevel, deps: deps, - onExpandToggle: (event: React.MouseEvent) => { + onExpandToggle: (event: React.MouseEvent) => { cardSelectionMessageCreator?.toggleRuleExpandCollapse(rule.id, event); }, isExpanded: rule.isExpanded, diff --git a/src/common/components/collapsible-component.scss b/src/common/components/collapsible-component.scss index d570ebce936..b5aec73b135 100644 --- a/src/common/components/collapsible-component.scss +++ b/src/common/components/collapsible-component.scss @@ -8,15 +8,18 @@ span.collapsible-title { } .collapsible { - padding: 0; cursor: pointer; - width: 100%; + width: auto; border: none; outline: none; line-height: 0.25px; display: flex; justify-content: flex-start; align-items: center; + padding-top: 5px; + padding-bottom: 5px; + padding-right: 27px !important; + padding-left: 1px !important; h2 { font-weight: $normal-title-font-weight; diff --git a/src/common/components/collapsible-component.tsx b/src/common/components/collapsible-component.tsx index 74f7e739002..e020f3cda5a 100644 --- a/src/common/components/collapsible-component.tsx +++ b/src/common/components/collapsible-component.tsx @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { ActionButton } from '@fluentui/react'; +import { Button } from '@fluentui/react-components'; import { css } from '@fluentui/utilities'; import styles from 'common/components/collapsible-component.scss'; +import { FluentUIV9Icon } from 'common/icons/fluentui-v9-icons'; import * as React from 'react'; export interface CollapsibleComponentProps { @@ -20,9 +21,6 @@ export class CollapsibleComponent extends React.Component< CollapsibleComponentProps, CollapsibleComponentState > { - private readonly iconNameDown = 'ChevronDown'; - private readonly iconNameUp = 'ChevronRight'; - constructor(props: CollapsibleComponentProps) { super(props); this.state = { showContent: true }; @@ -34,11 +32,9 @@ export class CollapsibleComponent extends React.Component< public render(): JSX.Element { const showContent = this.state.showContent; - let iconName = this.iconNameUp; let content: JSX.Element | null = null; if (showContent) { - iconName = this.iconNameDown; content = (
{this.props.content} @@ -48,14 +44,19 @@ export class CollapsibleComponent extends React.Component< return (
- + {showContent ? ( + + ) : ( + + )} {this.props.header} - + {content}
); diff --git a/src/common/components/controls/insights-command-button-style.tsx b/src/common/components/controls/insights-command-button-style.tsx new file mode 100644 index 00000000000..46448173caf --- /dev/null +++ b/src/common/components/controls/insights-command-button-style.tsx @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { makeStyles, tokens } from '@fluentui/react-components'; + +export const useInsightsCommandButtonStyle = makeStyles({ + button: { + fontWeight: tokens?.fontWeightRegular, + }, + + menuItem: { + border: 'unset !important', + }, +}); diff --git a/src/common/components/controls/insights-command-button.scss b/src/common/components/controls/insights-command-button.scss index 3be08255899..82e426e2bae 100644 --- a/src/common/components/controls/insights-command-button.scss +++ b/src/common/components/controls/insights-command-button.scss @@ -15,6 +15,10 @@ } } + &:hover { + background: none !important; + } + &:active:not(:disabled) { .command-bar-button-icon { color: $primary-text; diff --git a/src/common/components/controls/insights-command-button.tsx b/src/common/components/controls/insights-command-button.tsx index bd8b2f8e213..d9e0ac1cf69 100644 --- a/src/common/components/controls/insights-command-button.tsx +++ b/src/common/components/controls/insights-command-button.tsx @@ -1,25 +1,55 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { ActionButton, css, IButtonProps } from '@fluentui/react'; +import { mergeClasses, MenuButtonProps, Button, MenuItem } from '@fluentui/react-components'; + +import { useInsightsCommandButtonStyle } from 'common/components/controls/insights-command-button-style'; import { NamedFC } from 'common/react/named-fc'; import * as React from 'react'; import styles from './insights-command-button.scss'; -export type InsightsCommandButtonProps = IButtonProps; +export type InsightsCommandButtonIconProps = { + className?: string; + icon?: JSX.Element; +}; + +export type InsightsCommandButtonProps = (MenuButtonProps | any) & { + insightsCommandButtonIconProps?: InsightsCommandButtonIconProps; + isNarrowMode?: boolean; +}; // See https://www.figma.com/file/Wj4Ggf6GGQBQkiDIaHfXRX2B/Accessibility-Insights%3A-Styles?node-id=1%3A27 export const InsightsCommandButton = NamedFC( 'InsightsCommandButton', - props => { - return ( - { + const overrides = useInsightsCommandButtonStyle(); + return props.isNarrowMode ? ( + + {props.children} + + ) : ( + ); - }, + }), ); diff --git a/src/common/components/theme.tsx b/src/common/components/theme.tsx index f1d6f53e6c8..f228e9276e5 100644 --- a/src/common/components/theme.tsx +++ b/src/common/components/theme.tsx @@ -70,7 +70,10 @@ export class ThemeInner extends React.Component { private loadAppropriateTheme(isHighContrast: boolean): void { const appropriateThemeV8 = isHighContrast ? HighContrastTheme : DefaultTheme; const appropriateThemeV9 = isHighContrast ? ThemeV9DarkTheme : webLightTheme; - this.setState({ themeValueV8: appropriateThemeV8, themeValueV9: appropriateThemeV9 }); + this.setState({ + themeValueV8: appropriateThemeV8, + themeValueV9: appropriateThemeV9, + }); } private isHighContrastEnabled(props: ThemeInnerProps): boolean { diff --git a/src/common/icons/fluentui-v9-icons.tsx b/src/common/icons/fluentui-v9-icons.tsx new file mode 100644 index 00000000000..8471ec26417 --- /dev/null +++ b/src/common/icons/fluentui-v9-icons.tsx @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +import { makeStyles, mergeClasses, tokens } from '@fluentui/react-components'; +import { + ArrowExportRegular, + FolderArrowRightRegular, + FolderOpenRegular, + SaveRegular, + ArrowClockwiseRegular, + Checkmark20Filled, + ChevronDown20Regular, + ChevronRight20Regular, + InfoRegular, + DocumentCopyRegular, + BugFilled, + MoreVerticalRegular, + ChevronDown32Regular, + ChevronRight32Regular, + MoreHorizontalRegular, + AddRegular, + ChevronDown24Regular, + ChevronRight24Regular, +} from '@fluentui/react-icons'; +import { NamedFC } from 'common/react/named-fc'; +import { isUndefined } from 'lodash'; + +import React from 'react'; + +export const Icons = { + ArrowExportRegular: ArrowExportRegular, + FolderArrowRightRegular: FolderArrowRightRegular, + FolderOpenRegular: FolderOpenRegular, + SaveRegular: SaveRegular, + ArrowClockwiseRegular: ArrowClockwiseRegular, + Checkmark20Filled: Checkmark20Filled, + ChevronDown20Regular: ChevronDown20Regular, + ChevronRight20Regular: ChevronRight20Regular, + InfoRegular: InfoRegular, + BugFilled: BugFilled, + DocumentCopyRegular: DocumentCopyRegular, + MoreVerticalRegular: MoreVerticalRegular, + ChevronDown32Regular: ChevronDown32Regular, + ChevronRight32Regular: ChevronRight32Regular, + MoreHorizontalRegular: MoreHorizontalRegular, + AddRegular: AddRegular, + ChevronDown24Regular: ChevronDown24Regular, + ChevronRight24Regular: ChevronRight24Regular, +}; + +export const useIconStyles = makeStyles({ + refreshIcon: { + paddingLeft: '14px', + }, + IconTheme: { + color: tokens.colorCompoundBrandStrokeHover, + }, +}); + +export type FluentUIV9IconProps = { + iconName: string | any; + customClass?: string | any; +}; + +export const FluentUIV9Icon = NamedFC('FluentUIV9Icon', props => { + const styleClasses: any = useIconStyles(); + const isIconAvailable = !isUndefined(props?.iconName) && Icons[props.iconName]; + + if (isIconAvailable) { + const Icon = Icons[props?.iconName]; + return ; + } + + return null; +}); diff --git a/src/common/styles/theme-v9-dark-theme.ts b/src/common/styles/theme-v9-dark-theme.ts index 225f8bcf602..2e6469365db 100644 --- a/src/common/styles/theme-v9-dark-theme.ts +++ b/src/common/styles/theme-v9-dark-theme.ts @@ -1,6 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. - import { webDarkTheme } from '@fluentui/react-components'; // This is a copy of our ThemeDark from V9 slightly adjusted to account for the page background @@ -9,4 +8,6 @@ import { webDarkTheme } from '@fluentui/react-components'; export const ThemeV9DarkTheme = { ...webDarkTheme, colorNeutralBackground1: '#161616', + colorNeutralForeground2: '#fff', + colorCompoundBrandStrokeHover: '#ffffff', }; diff --git a/src/popup/Styles/popup.scss b/src/popup/Styles/popup.scss index 50ba7f6c024..e0f55af42cd 100644 --- a/src/popup/Styles/popup.scss +++ b/src/popup/Styles/popup.scss @@ -153,12 +153,11 @@ html { } #popup-container .ms-Link { + color: $communication-primary; + font-weight: 600; @media screen and (forced-colors: active) { color: linktext !important; } - - color: $communication-primary; - font-weight: 600; } #popup-container .main-section { diff --git a/src/reports/automated-checks-report.scss b/src/reports/automated-checks-report.scss index d8ddd79bc5b..06a2b93c72c 100644 --- a/src/reports/automated-checks-report.scss +++ b/src/reports/automated-checks-report.scss @@ -89,7 +89,7 @@ $outcome-not-applicable-summary-color: $neutral-outcome; border: none; display: flex; align-items: baseline; - width: 100%; + width: auto; &:hover { background-color: $neutral-alpha-4; diff --git a/src/reports/components/instance-details.scss b/src/reports/components/instance-details.scss index ea035bd6225..13f7e6f479f 100644 --- a/src/reports/components/instance-details.scss +++ b/src/reports/components/instance-details.scss @@ -80,6 +80,7 @@ padding-bottom: 14px; padding-left: 0; padding-right: 20px; + border-bottom: 0.5px solid $neutral-10; > * { margin-top: 12px; @@ -89,8 +90,6 @@ padding: 0; } - border-bottom: 0.5px solid $neutral-10; - &:last-child { border-bottom: none; } diff --git a/src/reports/components/outcome-summary-bar.scss b/src/reports/components/outcome-summary-bar.scss index 7990591b419..984a03786b2 100644 --- a/src/reports/components/outcome-summary-bar.scss +++ b/src/reports/components/outcome-summary-bar.scss @@ -51,6 +51,7 @@ $outcome-not-applicable-color: $neutral-outcome; } .fail { + background-color: $outcome-fail-color; @extend %block; @include cross-icon-styles($outcome-summary-icon-size, 0, $outcome-fail-color); @@ -58,11 +59,10 @@ $outcome-not-applicable-color: $neutral-outcome; bottom: -1px; margin-right: 8px; } - - background-color: $outcome-fail-color; } .pass { + background-color: $outcome-pass-color; @extend %block; @include check-icon-styles($outcome-summary-icon-size, 0, $outcome-pass-color); @@ -71,12 +71,11 @@ $outcome-not-applicable-color: $neutral-outcome; margin-right: 8px; margin-left: 4px; } - - background-color: $outcome-pass-color; } .inapplicable, .unscannable { + background-color: $outcome-not-applicable-color; @extend %block; @include inapplicable-icon-styles( $outcome-summary-icon-size, @@ -89,11 +88,13 @@ $outcome-not-applicable-color: $neutral-outcome; margin-right: 8px; margin-left: 4px; } - - background-color: $outcome-not-applicable-color; } .incomplete { + background-color: $outcome-incomplete-summary-background; + color: $outcome-incomplete-summary-consistent-foreground; + border: 2px $outcome-incomplete-border-color solid; + height: 12px; @extend %block; @include incomplete-icon-styles(14px, 3px); @@ -101,11 +102,6 @@ $outcome-not-applicable-color: $neutral-outcome; margin-right: 6px; border-color: $outcome-incomplete-summary-consistent-foreground; } - - background-color: $outcome-incomplete-summary-background; - color: $outcome-incomplete-summary-consistent-foreground; - border: 2px $outcome-incomplete-border-color solid; - height: 12px; } .summary-bar-left-edge { diff --git a/src/reports/components/report-sections/collapsible-result-section.tsx b/src/reports/components/report-sections/collapsible-result-section.tsx index 84a3dc5dcb8..c9b5a88a4fb 100644 --- a/src/reports/components/report-sections/collapsible-result-section.tsx +++ b/src/reports/components/report-sections/collapsible-result-section.tsx @@ -37,7 +37,7 @@ export const CollapsibleResultSection = NamedFC( content: , headingLevel: props.headingLevel, testKey, - onExpandToggle: (event: React.MouseEvent) => { + onExpandToggle: (event: React.MouseEvent) => { cardSelectionMessageCreator?.toggleRuleExpandCollapse(containerId, event); }, }); diff --git a/src/reports/components/report-sections/combined-report-rules-only-sections.tsx b/src/reports/components/report-sections/combined-report-rules-only-sections.tsx index ab0f99433f6..4d6ea6994ba 100644 --- a/src/reports/components/report-sections/combined-report-rules-only-sections.tsx +++ b/src/reports/components/report-sections/combined-report-rules-only-sections.tsx @@ -43,7 +43,7 @@ const makeCombinedReportRulesOnlySection = (options: { outcomeType={outcomeType} /> ), - onExpandToggle: (event: React.MouseEvent) => { + onExpandToggle: (event: React.MouseEvent) => { cardSelectionMessageCreator?.toggleRuleExpandCollapse(sectionId, event); }, headingLevel: 3, diff --git a/src/reports/components/report-sections/report-collapsible-container.tsx b/src/reports/components/report-sections/report-collapsible-container.tsx index 288a0016460..6407b6ae983 100644 --- a/src/reports/components/report-sections/report-collapsible-container.tsx +++ b/src/reports/components/report-sections/report-collapsible-container.tsx @@ -14,7 +14,7 @@ export interface ReportCollapsibleContainerProps { containerClassName?: string; buttonAriaLabel?: string; testKey?: string; - onExpandToggle?: (event: React.MouseEvent) => void; + onExpandToggle?: (event: React.MouseEvent) => void; } const ReportCollapsibleContainer = NamedFC( diff --git a/src/tests/end-to-end/common/pretty-print-axe-violations.ts b/src/tests/end-to-end/common/pretty-print-axe-violations.ts index fadf2ac027f..6b7c408cc08 100644 --- a/src/tests/end-to-end/common/pretty-print-axe-violations.ts +++ b/src/tests/end-to-end/common/pretty-print-axe-violations.ts @@ -28,6 +28,9 @@ export function normalizeSelector(selector: string): string { const identifier = output.slice(1); output = `${output[0]}${normalizeOfficeFabricClassName(identifier)}`; } + if (/i\S+nth-child\(\d\)/.test(output)) { + output = 'i:nth-child(0)'; + } return output; } diff --git a/src/tests/end-to-end/tests/details-view/__snapshots__/reflow-ui.test.ts.snap b/src/tests/end-to-end/tests/details-view/__snapshots__/reflow-ui.test.ts.snap index f1048a42543..22a4d8b89d6 100644 --- a/src/tests/end-to-end/tests/details-view/__snapshots__/reflow-ui.test.ts.snap +++ b/src/tests/end-to-end/tests/details-view/__snapshots__/reflow-ui.test.ts.snap @@ -4,9 +4,79 @@ exports[`Details View -> Assessment -> Reflow With command bar button visible sh exports[`Details View -> Assessment -> Reflow With command bar button visible should pass accessibility validation with high contrast mode=true 1`] = `[]`; -exports[`Details View -> Assessment -> Reflow With command bar button visible with command bar button expanded should pass accessibility validation with command bar menu open and high contrast mode=false 1`] = `[]`; +exports[`Details View -> Assessment -> Reflow With command bar button visible with command bar button expanded should pass accessibility validation with command bar menu open and high contrast mode=false 1`] = ` +[ + { + "id": "aria-hidden-focus", + "nodes": [ + { + "failureSummary": "Fix all of the following: + Focusable content should have tabindex="-1" or be removed from the DOM", + "selector": [ + "i:nth-child(0)", + ], + }, + { + "failureSummary": "Fix all of the following: + Focusable content should have tabindex="-1" or be removed from the DOM", + "selector": [ + "i:nth-child(0)", + ], + }, + ], + }, + { + "id": "aria-required-children", + "nodes": [ + { + "failureSummary": "Fix any of the following: + Element has children which are not allowed: button[tabindex], [role=button] + Element uses aria-busy="true" while showing a loader", + "selector": [ + ".fui-MenuList", + ], + }, + ], + }, +] +`; -exports[`Details View -> Assessment -> Reflow With command bar button visible with command bar button expanded should pass accessibility validation with command bar menu open and high contrast mode=true 1`] = `[]`; +exports[`Details View -> Assessment -> Reflow With command bar button visible with command bar button expanded should pass accessibility validation with command bar menu open and high contrast mode=true 1`] = ` +[ + { + "id": "aria-hidden-focus", + "nodes": [ + { + "failureSummary": "Fix all of the following: + Focusable content should have tabindex="-1" or be removed from the DOM", + "selector": [ + "i:nth-child(0)", + ], + }, + { + "failureSummary": "Fix all of the following: + Focusable content should have tabindex="-1" or be removed from the DOM", + "selector": [ + "i:nth-child(0)", + ], + }, + ], + }, + { + "id": "aria-required-children", + "nodes": [ + { + "failureSummary": "Fix any of the following: + Element has children which are not allowed: button[tabindex], [role=button] + Element uses aria-busy="true" while showing a loader", + "selector": [ + ".fui-MenuList", + ], + }, + ], + }, +] +`; exports[`Details View -> Assessment -> Reflow With hamburger button visible should pass accessibility validation with high contrast mode=false 1`] = `[]`; @@ -16,13 +86,105 @@ exports[`Details View -> Assessment -> Reflow With hamburger button visible with exports[`Details View -> Assessment -> Reflow With hamburger button visible with hamburger button expanded should pass accessibility validation with command bar menu open and high contrast mode=true 1`] = `[]`; -exports[`Details View -> Fastpass -> Reflow With high contrast mode=false With command bar button visible with command bar button expanded should pass accessibility validation with command bar menu open and high contrast mode=%s 1`] = `[]`; +exports[`Details View -> Fastpass -> Reflow With high contrast mode=false With command bar button visible with command bar button expanded should pass accessibility validation with command bar menu open and high contrast mode=%s 1`] = ` +[ + { + "id": "aria-hidden-focus", + "nodes": [ + { + "failureSummary": "Fix all of the following: + Focusable content should have tabindex="-1" or be removed from the DOM", + "selector": [ + "i:nth-child(0)", + ], + }, + { + "failureSummary": "Fix all of the following: + Focusable content should have tabindex="-1" or be removed from the DOM", + "selector": [ + "i:nth-child(0)", + ], + }, + ], + }, + { + "id": "aria-required-children", + "nodes": [ + { + "failureSummary": "Fix any of the following: + Element has children which are not allowed: button[tabindex] + Element uses aria-busy="true" while showing a loader", + "selector": [ + ".fui-MenuList", + ], + }, + ], + }, +] +`; exports[`Details View -> Fastpass -> Reflow With high contrast mode=false and card kebab menu collapsed 1`] = `[]`; -exports[`Details View -> Fastpass -> Reflow With high contrast mode=false and card kebab menu expanded 1`] = `[]`; +exports[`Details View -> Fastpass -> Reflow With high contrast mode=false and card kebab menu expanded 1`] = ` +[ + { + "id": "aria-hidden-focus", + "nodes": [ + { + "failureSummary": "Fix all of the following: + Focusable content should have tabindex="-1" or be removed from the DOM", + "selector": [ + "i:nth-child(0)", + ], + }, + { + "failureSummary": "Fix all of the following: + Focusable content should have tabindex="-1" or be removed from the DOM", + "selector": [ + "i:nth-child(0)", + ], + }, + ], + }, +] +`; -exports[`Details View -> Fastpass -> Reflow With high contrast mode=true With command bar button visible with command bar button expanded should pass accessibility validation with command bar menu open and high contrast mode=%s 1`] = `[]`; +exports[`Details View -> Fastpass -> Reflow With high contrast mode=true With command bar button visible with command bar button expanded should pass accessibility validation with command bar menu open and high contrast mode=%s 1`] = ` +[ + { + "id": "aria-hidden-focus", + "nodes": [ + { + "failureSummary": "Fix all of the following: + Focusable content should have tabindex="-1" or be removed from the DOM", + "selector": [ + "i:nth-child(0)", + ], + }, + { + "failureSummary": "Fix all of the following: + Focusable content should have tabindex="-1" or be removed from the DOM", + "selector": [ + "i:nth-child(0)", + ], + }, + ], + }, + { + "id": "aria-required-children", + "nodes": [ + { + "failureSummary": "Fix any of the following: + Element has children which are not allowed: button[tabindex] + Element uses aria-busy="true" while showing a loader", + "selector": [ + ".fui-MenuList", + ], + }, + ], + }, +] +`; exports[`Details View -> Fastpass -> Reflow With high contrast mode=true and card kebab menu collapsed 1`] = ` [ @@ -42,15 +204,107 @@ exports[`Details View -> Fastpass -> Reflow With high contrast mode=true and car ] `; -exports[`Details View -> Fastpass -> Reflow With high contrast mode=true and card kebab menu expanded 1`] = `[]`; +exports[`Details View -> Fastpass -> Reflow With high contrast mode=true and card kebab menu expanded 1`] = ` +[ + { + "id": "aria-hidden-focus", + "nodes": [ + { + "failureSummary": "Fix all of the following: + Focusable content should have tabindex="-1" or be removed from the DOM", + "selector": [ + "i:nth-child(0)", + ], + }, + { + "failureSummary": "Fix all of the following: + Focusable content should have tabindex="-1" or be removed from the DOM", + "selector": [ + "i:nth-child(0)", + ], + }, + ], + }, +] +`; exports[`Details View -> Quick Assess -> Reflow With command bar button visible should pass accessibility validation with high contrast mode=false 1`] = `[]`; exports[`Details View -> Quick Assess -> Reflow With command bar button visible should pass accessibility validation with high contrast mode=true 1`] = `[]`; -exports[`Details View -> Quick Assess -> Reflow With command bar button visible with command bar button expanded should pass accessibility validation with command bar menu open and high contrast mode=false 1`] = `[]`; +exports[`Details View -> Quick Assess -> Reflow With command bar button visible with command bar button expanded should pass accessibility validation with command bar menu open and high contrast mode=false 1`] = ` +[ + { + "id": "aria-hidden-focus", + "nodes": [ + { + "failureSummary": "Fix all of the following: + Focusable content should have tabindex="-1" or be removed from the DOM", + "selector": [ + "i:nth-child(0)", + ], + }, + { + "failureSummary": "Fix all of the following: + Focusable content should have tabindex="-1" or be removed from the DOM", + "selector": [ + "i:nth-child(0)", + ], + }, + ], + }, + { + "id": "aria-required-children", + "nodes": [ + { + "failureSummary": "Fix any of the following: + Element has children which are not allowed: button[tabindex], [role=button] + Element uses aria-busy="true" while showing a loader", + "selector": [ + ".fui-MenuList", + ], + }, + ], + }, +] +`; -exports[`Details View -> Quick Assess -> Reflow With command bar button visible with command bar button expanded should pass accessibility validation with command bar menu open and high contrast mode=true 1`] = `[]`; +exports[`Details View -> Quick Assess -> Reflow With command bar button visible with command bar button expanded should pass accessibility validation with command bar menu open and high contrast mode=true 1`] = ` +[ + { + "id": "aria-hidden-focus", + "nodes": [ + { + "failureSummary": "Fix all of the following: + Focusable content should have tabindex="-1" or be removed from the DOM", + "selector": [ + "i:nth-child(0)", + ], + }, + { + "failureSummary": "Fix all of the following: + Focusable content should have tabindex="-1" or be removed from the DOM", + "selector": [ + "i:nth-child(0)", + ], + }, + ], + }, + { + "id": "aria-required-children", + "nodes": [ + { + "failureSummary": "Fix any of the following: + Element has children which are not allowed: button[tabindex], [role=button] + Element uses aria-busy="true" while showing a loader", + "selector": [ + ".fui-MenuList", + ], + }, + ], + }, +] +`; exports[`Details View -> Quick Assess -> Reflow With hamburger button visible should pass accessibility validation with high contrast mode=false 1`] = `[]`; diff --git a/src/tests/end-to-end/tests/details-view/reflow-ui.test.ts b/src/tests/end-to-end/tests/details-view/reflow-ui.test.ts index e4765658a49..84f7fba12ae 100644 --- a/src/tests/end-to-end/tests/details-view/reflow-ui.test.ts +++ b/src/tests/end-to-end/tests/details-view/reflow-ui.test.ts @@ -239,6 +239,12 @@ describe('Details View ->', () => { // this is a false positive because background is black and link is having yellow as per high contrast theme // standards so this criteria does not apply // Please refer for more information on the error:https://github.com/dequelabs/axe-core/issues/3464 + // this results object has a failure for aria-hidden-focus + // These are Tabster dummy inputs and they must be added to the exception list. + // They are intentionally aria - hidden as their purpose is to redirect focus to the correct element + // right when they receive focus and not having aria - hidden will result in the screen readers choking + // starting to announce them. + // Please refer for more information on the error:https://github.com/microsoft/fluentui/issues/25133 expect(results).toMatchSnapshot(); } diff --git a/src/tests/unit/tests/DetailsView/components/__snapshots__/assessment-instance-table.test.tsx.snap b/src/tests/unit/tests/DetailsView/components/__snapshots__/assessment-instance-table.test.tsx.snap index da7dfa6aaae..2fbf380a138 100644 --- a/src/tests/unit/tests/DetailsView/components/__snapshots__/assessment-instance-table.test.tsx.snap +++ b/src/tests/unit/tests/DetailsView/components/__snapshots__/assessment-instance-table.test.tsx.snap @@ -6,7 +6,7 @@ exports[`AssessmentInstanceTable with instances renders per snapshot 1`] = ` Pass unmarked instances @@ -72,8 +72,10 @@ exports[`AssessmentInstanceTable with instances renders per snapshot: InsightsCo "children": "Pass unmarked instances", "data-automation-id": "assessment-instance-table-pass-unmarked-instances-button", "disabled": false, - "iconProps": { - "iconName": "skypeCheck", + "insightsCommandButtonIconProps": { + "icon": , }, "onClick": [Function], } diff --git a/src/tests/unit/tests/DetailsView/components/__snapshots__/command-bar-buttons-menu.test.tsx.snap b/src/tests/unit/tests/DetailsView/components/__snapshots__/command-bar-buttons-menu.test.tsx.snap index 5173525f82c..ec3b7afef46 100644 --- a/src/tests/unit/tests/DetailsView/components/__snapshots__/command-bar-buttons-menu.test.tsx.snap +++ b/src/tests/unit/tests/DetailsView/components/__snapshots__/command-bar-buttons-menu.test.tsx.snap @@ -2,97 +2,66 @@ exports[`CommandBarButtonsMenu renders CommandBarButtonsMenu 1`] = ` - - - + + + + + + + + + Save assessment button + + + Load assessment button + + + Transfer to assessment button + + + + + `; -exports[`CommandBarButtonsMenu renders CommandBarButtonsMenu: CustomizedCommandBarButton props 1`] = ` -{ - "ariaLabel": "More actions", - "className": "commandBarButtonsMenu", - "componentRef": {}, - "menuIconProps": { - "className": "commandBarButtonsMenuButton", - "iconName": "More", - }, - "menuProps": { - "className": "commandBarButtonsSubmenu", - "items": [ - { - "key": "save assessment", - "onRender": [Function], - }, - { - "key": "load assessment", - "onRender": [Function], - }, - { - "key": "transfer to assessment", - "onRender": [Function], - }, - { - "key": "start over", - }, - ], - }, - "role": "button", -} +exports[`CommandBarButtonsMenu renders all child buttons with hasSubMenu false,: render export report menuitem 1`] = ` + + Report export button + `; -exports[`CommandBarButtonsMenu renders all child buttons,: render export report menuitem 1`] = ` -
- - Report export button - -
+exports[`CommandBarButtonsMenu renders all child buttons with hasSubMenu false,: render load assessment menuitem 1`] = ` + + Load assessment button + `; -exports[`CommandBarButtonsMenu renders all child buttons,: render load assessment menuitem 1`] = ` -
- - Load assessment button - -
+exports[`CommandBarButtonsMenu renders all child buttons with hasSubMenu false,: render save assessment menuitem 1`] = ` + + Save assessment button + `; -exports[`CommandBarButtonsMenu renders all child buttons,: render save assessment menuitem 1`] = ` -
- - Save assessment button - -
-`; - -exports[`CommandBarButtonsMenu renders all child buttons,: render start over menuitem 1`] = ` - - Start over button - -`; - -exports[`CommandBarButtonsMenu renders all child buttons,: render transfer to assessment menuitem 1`] = ` -
- - Transfer to assessment button - -
+exports[`CommandBarButtonsMenu renders all child buttons with hasSubMenu false,: render transfer to assessment menuitem 1`] = ` + + Transfer to assessment button + `; diff --git a/src/tests/unit/tests/DetailsView/components/__snapshots__/details-view-command-bar.test.tsx.snap b/src/tests/unit/tests/DetailsView/components/__snapshots__/details-view-command-bar.test.tsx.snap index 6bab4280c53..bcc6451902e 100644 --- a/src/tests/unit/tests/DetailsView/components/__snapshots__/details-view-command-bar.test.tsx.snap +++ b/src/tests/unit/tests/DetailsView/components/__snapshots__/details-view-command-bar.test.tsx.snap @@ -1,5 +1,271 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`DetailsViewCommandBar Button focus render statover component 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+
+
+
+ + Target page:  + + + + command-bar-test-tab-title + + +
+ + + + +
+
+ , + "container":
+
+
+ + Target page:  + + + + command-bar-test-tab-title + + +
+ + + + +
+
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + exports[`DetailsViewCommandBar renders invalid load assessment dialog: invalid load assessment dialog hidden 1`] = `