I've been thinking about what can be done as part of a version 2.0 rewrite of the CSS adapters, and over the past few nights did some test coding to see what works and what doesn't.
First, let me outline some of the things that I feel should be a key part of version 2.0.
- Improved testability. The source code should include unit tests.
- Improved rendering. Wherever possible, the code generated should be improved.
- Improved configuration. There's limited configuration options right now unless you change the source code.
- Improved documentation. The typical need of an open source project.
With these things in mind, and after some experimentation using a simple control (CheckBoxList), I came up with a few ideas...
Improving Configuration
The obvious choice is to use the web.config file, but what should be allowed in the configuration? The first obvious choice is CSS class names. For example, consider the following mock configuration:
<CSSFriendly>
<CheckBoxList cssClass="AspNet-CheckBoxList"
disabledCssClass="AspNet-CheckBoxList-Disabled"
itemCssClass="AspNet-CheckBoxList-Item"
repeatDirectionCssClass="AspNet-CheckBoxList-RepeatDirection-" />
</CSSFriendly>
Note the <CSSFriendly> configuration section, which includes a <CheckBoxList /> element that allows you to edit the generated CSS classes. Say you don't want the default? You can specify this as a configuration:
<CSSFriendly>
<CheckBoxList cssClass="checkbox"
disabledCssClass="disabled"
itemCssClass=""
repeatDirectionCssClass="repeat-" />
</CSSFriendly>
In this case, you have new class names. An attribute that is blank (itemCssClass) would not get a class applied. In the CheckBoxList case, the repeatDirectionCssClass would append the repeat direction of the control. Or something like that... Remember, this is just a first concept!
Improving Testability
There are two ways to test web controls: to look at the HTML code they generate, or to do user behavior testing (possibly automated using something like Selenium). The latter is complex, but the former is not -- if you can somehow extract the HTML code generated by an adapter.
Unfortunately, this is easier said than done, so I started thinking about a new approach, which became a different way to look at how the adapters worked.
The adapters really do two things:
- Generate HTML markup
- Apply WebForms-specific scripts (such as Page.ClientScript.RegisterForEventValidation(checkList.UniqueID)).
My thinking is to split the HTML generation out of the adapter by deriving everything from two abstract classes: CssFriendlyAdapter<T> and HtmlRenderer<T>. (In both cases, T is a WebControl.)
The CssFriendlyAdapter would have an abstract method, CreateHtmlRenderer(). That method would be responsible for creating an HtmlRenderer for the adapter. The HtmlRenderer is responsible for making the HTML markup for a given WebControl; the adapter serves as a wrapper around its renderer, and can still apply WebForms extras.
This improves testability because we can now test our HtmlRenderer classes to ensure they generate the expected HTML markup for a given control. Since the HtmlRenderer is easy to instantiate and has limited dependencies, it is easy to do so.
The flow looks something like this:
WebControl as T
-> CSSFriendlyAdapter<T>
.OnPreRender()
-> CreateHtmlRenderer(); [abstract]
<- HtmlRenderer<T>
.RenderBeginTag()
-> _renderer.RenderBeginTag();
.RenderContents()
-> _renderer.RenderContents();
.RenderEndTag()
-> _renderer.RenderEndTag();
HtmlRenderer -- renders basic HTML
CssAdapter -- adds WebForms specific features
The following code is a sample of an NUnit test that actually works in my current sandbox.
[TestFixture]
public class CheckboxListHtmlRendererTests
{
private StringWriter stringwriter;
private HtmlTextWriter htmlwriter;
[SetUp]
public void SetUp()
{
stringwriter = new StringWriter();
htmlwriter = new HtmlTextWriter(stringwriter);
}
[Test]
public void TestValidHtml()
{
CheckBoxList control = new CheckBoxList();
control.ID = "test";
CheckBoxListHtmlRenderer renderer = new CheckBoxListHtmlRenderer(control);
Assert.AreEqual(renderer.RenderBeginTag(htmlwriter),
"<ul class=\"AspNet-CheckBoxList AspNet-CheckBoxList-RepeatDirection-Vertical\" id=\"test\">\r\n");
}
}
Cool stuff.
Improved Rendering and Documentation
Obviously, any effort in rewriting the controls should include both improving the rendered HTML and the available documentation. For the latter, my idea is to document as we go, and provide a matrix for each adapter that illustrates what features work for a control.
Documentation is something that, if done early and continuously, is easier to manage. Note that when I say "documentation" I am thinking moreso of API documentation and implementation documentation -- that is, how an adapter will change markup and what control properties are supported by it.
Not sure when I'll have something for public consumption -- at this point, I am just experimenting with one control (CheckBoxList) before moving to more complex objects.
Comments? Feedback? Anyone interested in contributing?