-
Notifications
You must be signed in to change notification settings - Fork 153
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
Conversation
Codecov Report
@@ 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
Continue to review full report at Codecov.
|
There was a problem hiding this 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 |
There was a problem hiding this comment.
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 = () => { |
There was a problem hiding this comment.
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'); |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
030a46b
to
840c067
Compare
Hey @sean-beard I noticed you pushed some new changes. Should I go ahead and review them? |
Hey @smoralesd, sure, feel free to review! |
c2e964d
to
11a4947
Compare
/azp run |
Azure Pipelines successfully started running 1 pipeline(s). |
There was a problem hiding this 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 theExportDialog
(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>) => { |
There was a problem hiding this comment.
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={() => { |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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>( |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
11a4947
to
6d1298f
Compare
/azp run |
Azure Pipelines successfully started running 1 pipeline(s). |
6d1298f
to
c6e47fb
Compare
c6e47fb
to
eba86b2
Compare
There was a problem hiding this 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 :)
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
fromoffice-ui-fabric-react
in theExportDialog
component but I'm definitely open to alternative approaches!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
yarn fastpass
yarn test
)<rootDir>/test-results/unit/coverage
fix:
,chore:
,feat(feature-name):
,refactor:
). Check workflow guide at:<rootDir>/docs/workflow.md