The FlexGrid, first developed 20 years ago, has evolved with our customer base, moving into new platforms, devices, and frameworks as the market demands. We’ve recently developed a new feature, Sticky Headers, with one of our customers.
While it was inspired by and developed with one of our largest customers, it benefits all of our customers, and as a result, Wijmo as a product. Here’s a look at how we collaborated to develop Sticky Headers.
Some businesses are product-driven, and some are customer-driven. In a nutshell, a product-driven business is introducing a brand-new concept, a wild innovation, into the market.
The product doesn’t even have any competition, and generally, the innovators aren’t asking for customer feedback because the customers haven’t caught up with the innovation. The most obvious example is the iPod: it was a brand-new solution to an existing problem.
Product-driven companies tend to become customer-driven, a healthy process for all successful products.
Over time, though, the iPod has evolved, as all successful products do, and development evolves, as well. Product-driven companies tend to become customer-driven, a healthy process for all successful products. It’s a rewarding transition for companies and customers alike, who benefit from constant product improvements.
We get a lot of great ideas from our customers, and we curate them: We estimate how many customers benefit from each idea and how much effort it would take to implement. This is the customer-driven part of the exercise. We also analyze how to do it in a way that is consistent with the original “spirit” of the product.
This is the product-driven part. If the idea passes this first evaluation, we normally implement it as a public sample, and listen to the customer feedback. The most popular ideas eventually get “promoted” and are moved into the product itself.
One of our largest customers recently asked us to help them implement a new feature in the FlexGrid. They wanted something called “sticky headers”: they wanted to be able to scroll a page and still see the grid’s column headers as the page scrolled off the screen.
We really liked the idea because it seemed like something many users could benefit from, and it seemed like something that could be done by adding a single property to the grid (stickyHeaders = true, presto). The image below shows the result:
When the user scrolls, the header stays in place The grid on the left has the stickyHeaders property set to true, so the column headers remain visible as the user scrolls the document, making the information on the grid easier to understand. We use some CSS to make “stuck” headers partially transparent. We were excited to show it to the customer and they really liked it, but it wasn’t enough.
While the headers remained visible as the user scrolled the document and the grid went off the screen, it didn’t work in cases where the grid was hosted in other scrollable elements. This was an important scenario because they used the grid as part of several re-usable components that could be hosted in arbitrary (and possibly scrollable) elements.
We improved the original implementation and extended it so the headers remained visible within any hierarchy of scrollable elements. We also optimized the original implementation to improve the performance and smooth scrolling. The object model did not change at all; we still had a single property. This time, the grids weren’t hosted in the document body, but in a scrollable container. The sticky headers work exactly as they did before:
The header sticks even in other scrolling elements We showed this second iteration to the customer and they liked it, but it still wasn’t enough. One of their teams wanted to add a toolbar above the grid, and they wanted the toolbar (or any arbitrary element) to inherit the sticky behavior as well.
We went back to work and decided to create a sample demonstrating how to implement the customer’s requirement using the feature as we had it. The initial idea was a little kludgy, but simple and adequate as a proof-of-concept. We would:
The idea worked OK, but it did take a bit of code to implement (3.5k bytes of TypeScript), and it had some shortcomings. Hosting the toolbar in a grid header cell caused some interference with the grid’s mouse handling (you could no longer click the top-left cell to select all cells, because the toolbar was in the way).
We sent the sample to the customer anyway, and they quickly extended it to test some of the ideas they had. For example, they wanted to host a collapsible GroupPanel within the sticky toolbar. This seemed like another great idea, but it also caused interferences between the grid’s and the GroupPanel’s mouse handling (both use drag-and-drop). So once again, they liked it but it was still not good enough (actually, we didn’t like this solution much either).
We went back to work, this time armed with a new GroupPanel requirement. This time we came up with a much simpler solution: Instead of hosting the toolbar element in the grid’s header cells, we inserted it at the top of the grid’s element tree.
This solved all the mouse interference problems, eliminated the need to create and update the new row, and simplified things a lot. The code size dropped from the original 3.5k down to 600 bytes. We did have to make a small change to the stickyHeaders implementation in the grid itself, which consisted of raising the updatingLayout and updatedLayout events (which already existed) when updating the position of the sticky headers.
This small change enhanced the usefulness of the stickyHeaders property by enabling useful scenarios that we hadn’t foreseen in the original implementation. We also updated our sample to include a collapsible GroupPanel and a filter, both of which can be toggled by clicking buttons on the toolbar. The image below shows the result:
Other header elements stay in place The purple bar is the sticky header. It stays above the grid and remains visible as the user scrolls the document or any scrollable ancestor of the grid, along with the column headers.
Clicking the “hamburger” button on the right of the toolbar toggles the GroupPanel that is part of the toolbar. We sent the updated sample to the customer, and this time they really liked it.
The stickyHeaders implementation was improved, and we released a new “StickyHeaders” sample that shows how you can leverage the feature in simple and advanced scenarios.
The whole process took about a month and four rounds of interaction, and the result was just a few lines of code. We’re happy we could take the time to make it this flexible and concise, and grateful to the customer who took the time to work with us to tune this feature and make it just right.
This is a win-win process, one we try to repeat as often as possible. The customer gets the features they want, implemented to their exact requirements. We get their valuable domain knowledge in the form of requirements, and use it to improve our products. Other customers with similar needs also benefit from the new and enhanced features.