Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(report): support ability to export to CodePen #2252

Merged
merged 23 commits into from
Apr 14, 2020

Conversation

sean-beard
Copy link
Contributor

Description of changes

These changes add the option to export reports to CodePen in addition to an HTML file. There is a relevant open issue detailing a need for this type of functionality.

I wasn't quite sure what the design should look like for this so I've went ahead and used a split PrimaryButton from office-ui-fabric-react in the ExportDialog component but I'm definitely open to alternative approaches!

a11y-report-export

I was also considering splitting out the HTML and CSS before sending the payload to CodePen but I figured I'd get some feedback on the changes within this PR before moving forward with more in-depth functionality.

Pull request checklist

  • Addresses an existing issue: export: provide hosted export targets #1739
  • Ran yarn fastpass
  • Added/updated relevant unit test(s) (and ran yarn test)
  • Verified code coverage for the changes made. Check coverage report at: <rootDir>/test-results/unit/coverage
  • PR title AND final merge commit title both start with a semantic tag (fix:, chore:, feat(feature-name):, refactor:). Check workflow guide at: <rootDir>/docs/workflow.md
  • (UI changes only) Added screenshots/GIFs to description above
  • (UI changes only) Verified usability with NVDA/JAWS

@sean-beard sean-beard requested a review from a team as a code owner February 28, 2020 21:36
@msftclas
Copy link

msftclas commented Feb 28, 2020

CLA assistant check
All CLA requirements met.

@codecov-io
Copy link

codecov-io commented Feb 28, 2020

Codecov Report

Merging #2252 into master will decrease coverage by 0.07%.
The diff coverage is 52.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2252      +/-   ##
==========================================
- Coverage   95.75%   95.68%   -0.08%     
==========================================
  Files         962      963       +1     
  Lines       14944    14968      +24     
  Branches      999     1002       +3     
==========================================
+ Hits        14310    14322      +12     
- Misses        513      523      +10     
- Partials      121      123       +2     
Impacted Files Coverage Δ
...-export/services/codepen-report-export-service.tsx 33.33% <33.33%> (ø)
src/DetailsView/components/export-dialog.tsx 86.66% <69.23%> (-13.34%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 02291cf...6d1298f. Read the comment docs.

Copy link
Contributor

@smoralesd smoralesd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @sean-beard, thank you so much for deciding to contribute to Accessibility Insights for Web, this is one of the first feature request contributions we have and the team is super excited to see this through.

We'd like to tackle this from 2 different angles: UI and code. I have started internal conversations to help move this forward on both fronts.

On one hand, yours it's a very good first approach that will allow us to have a short back-and-forth loop about how the UI should look in the finalized feature.

On the other hand, I channeled the team comments around the code. We always try to keep a high bar and we expect addressing this comments will allows us (you and the team) to keep (or even improve) the level of quality.

If you have any questions, please don't hesitate reaching out.

Sebastián.

</PrimaryButton>
<PrimaryButton
text="Export"
primary
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The primary option is not needed. The <PrimaryButton> already handles that.

@@ -67,6 +67,45 @@ export class ReportExportComponent extends React.Component<
this.setState({ exportDescription, exportName, isOpen: true });
};

private exportToCodePen = () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great opportunity to introduce a Strategy Pattern on how we export the report.
One strategy will be "export html report to file (in the local system)"; the second strategy will be "export html report to code pen".
Handling the different approaches with a Strategy Pattern allows to make <ReportExportComponent> open/close, i.e. we enable ourselves to add more export strategies without the need to modify this component.
We probably need a container so we can use the ExportFormat enum to get the right strategy to apply when the user selects the export button or any of the options available on the contextual menu of the button.

const { htmlGenerator } = this.props;
const { exportName, exportDescription } = this.state;

const form = document.createElement('form');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We avoid using global dependencies as much as we can. Using document in here makes this component to "be aware" it's running in a browser, and even though that's a fair assumption, it's also an easy one to avoid. Not assuming the code is running on a full-fledged browser allow for at least to things: 1) running the code not in a browser (like in a test run scenario without the need of any polyfill); 2) make it easier to unit test, as you don't need to rely on the test framework or any other library to provide for the document object; in this scenario, having a mock of whatever class we end up using will suffice.

That being said, if we decide to move forward with the Strategy pattern mention on one of my other comments, this code should be move to the ExportToCodePenStrategy class. We'll still need to not directly use document but ExportToCodePenStrategy can receive a FormFactory if you will.

};

private onExportClick = (format: ExportFormat) => {
switch (format) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use the Strategy Pattern, then onExportClick does not need to have the switch statement here. As long as we have a mean to retrieve the appropriate strategy base on format: ExportFormat we should be good.

@sean-beard sean-beard force-pushed the sb/eval-export-codepen branch 2 times, most recently from 030a46b to 840c067 Compare March 6, 2020 23:28
@smoralesd
Copy link
Contributor

Hey @sean-beard I noticed you pushed some new changes. Should I go ahead and review them?

@sean-beard
Copy link
Contributor Author

sean-beard commented Mar 13, 2020

Hey @smoralesd, sure, feel free to review!

@sean-beard sean-beard force-pushed the sb/eval-export-codepen branch 2 times, most recently from c2e964d to 11a4947 Compare March 16, 2020 01:42
@smoralesd
Copy link
Contributor

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Contributor

@smoralesd smoralesd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like very good progress.
I have a couple of concerns (in general):

  • Global dependencies (like how we are using ReportExportServiceProviderImpl in the ExportDialog (left a comment there)
  • React hooks introduction: this is something we have talked about in the team but this will be the first time someone actually use hooks. Some of the main concerns are (again) global dependencies and how to properly unit test this. Left a comment on a file with more details.

items: ReportExportServiceProviderImpl.all().map(service => ({
key: service.key,
text: service.displayName,
onClick: (e: React.MouseEvent<HTMLButtonElement>) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This onClick function does not seem to be covered by unit test.

fileName={props.fileName}
description={props.description}
html={props.html}
onSubmit={() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The onSubmit function here does not seem to be covered by unit test

@@ -27,18 +29,24 @@ export interface ExportDialogDeps {
}

export const ExportDialog = NamedFC<ExportDialogProps>('ExportDialog', props => {
const [format, setFormat] = React.useState<ExportFormat | null>(null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@waabid @dbjorge This is the first time we'll be introducing a react hook.
I'm not against this but I do have concerns, specially the fact that this is a global dependency and how to properly test the state changes (I'm guessing the testing part is a solved problem at this point but I may be wrong).
Any thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a particularly strong opinion on hooks vs class components as concepts, but I think it's better for maintainability to try to be consistent within a given codebase; I would lean towards using a class component instead for consistency's sake.


const CodePenReportExportServiceKey: ExportFormat = 'codepen';

const exportForm = NamedFC<ExportFormProps>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exportForm seems interesting enough that we should have unit tests for it.
The use of react hooks calls my attention here: currently on the repo, we don't use hooks so we don't have any example on how to unit tests this. It would be a very good contribution to write the first unit test around react hooks! If that seems to convoluted, there is always the possibility to go back to a more "traditional" react component so we can follow other examples in this repo on how to test it.

@@ -48,6 +56,8 @@ export const ExportDialog = NamedFC<ExportDialogProps>('ExportDialog', props =>
};

const fileURL = props.deps.fileURLProvider.provideURL([props.html], 'text/html');
const exportService = ReportExportServiceProviderImpl.forKey(format);
Copy link
Contributor

@smoralesd smoralesd Mar 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReportExportServiceProviderImpl is being use as a global dependency. Among other unwanted side effects, this forces the unit test to not be properly isolated (by depending on a concrete class/instance/implementation that's not the unit being tested).
One way to solve this is to follow the "deps pattern" we have across the repo:
Add a new property under ExportDialogDeps where we can pass an instance of the ReportExportServiceProvider. For the extension itself, this instance will be ReportExportServiceProviderImpl but for the unit test it can be a mock which does not need to change every time ReportExportServicePRoviderImpl changes, making the unit test properly isolated of other implementation details.

@sean-beard sean-beard force-pushed the sb/eval-export-codepen branch from 11a4947 to 6d1298f Compare March 21, 2020 03:55
@smoralesd
Copy link
Contributor

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@sean-beard sean-beard force-pushed the sb/eval-export-codepen branch from 6d1298f to c6e47fb Compare March 28, 2020 18:42
@sean-beard sean-beard force-pushed the sb/eval-export-codepen branch from c6e47fb to eba86b2 Compare March 31, 2020 23:12
Copy link
Contributor

@dbjorge dbjorge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again for the contribution, Sean; apologies for the delays in getting it merged! You can expect to see it as a "Preview Feature" in the next release :)

@dbjorge dbjorge dismissed smoralesd’s stale review April 14, 2020 00:27

Taking over from Sebastian

@dbjorge dbjorge merged commit 1c8ff9b into microsoft:master Apr 14, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants