-
Notifications
You must be signed in to change notification settings - Fork 12
Conversation
Codecov Report
@@ Coverage Diff @@
## master #37 +/- ##
==========================================
+ Coverage 98.17% 98.24% +0.07%
==========================================
Files 10 10
Lines 219 228 +9
Branches 34 36 +2
==========================================
+ Hits 215 224 +9
Partials 4 4
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.
For the things I review for typically, this looks fine, though someone else should review and approve before it lands. Thanks!
src/interfaces.d.ts
Outdated
export interface Column<T> { | ||
import { DNode } from '@dojo/widget-core/interfaces'; | ||
|
||
export interface Column<T = any, V = string> { |
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 still don't see the need for id
- field
is enough.
} | ||
|
||
export interface HasValue { | ||
value: string; | ||
} | ||
|
||
export interface ItemProperties<T> { | ||
export interface ItemProperties<T = any> { |
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.
Am I understanding correctly that dgrid requires data objects to look like this:
{
id: <idValue>,
data: {
<... data properties>
}
}
Are we not able to continue dgrid 1's design of accepting flatter objects (with support for idProperty
as well):
{
id: <idValue>,
<... data properties>
}
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.
Right now, the object structure is simple, but ItemProperties
adds much more moving forward.
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 think it would be very good if dgrid 2 is able to continue supporting data in the same format as dgrid 1 unless there's a compelling reason not to - is there?
While Dojo's data solutions have evolved over the years, the basic format of a data object has been consistent for years. What is the compelling need to change it?
Also - considering dgrid 2 requires a data provider, are my concerns relevant? Can one store be used for consumers that like the old data format, and also for dgrid, with the differences implemented in the data provider?
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 doesn't really have anything to do with the data - it's what the data provider gives to the grid. ArrayDataProvider
still supports simple data with idProperty
.
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.
ok great!
import { theme, ThemeableMixin, ThemeableProperties } from '@dojo/widget-core/mixins/Themeable'; | ||
|
||
import * as cellCss from './styles/shared/cell.m.css'; | ||
import * as css from './styles/cell.m.css'; | ||
|
||
export interface CellProperties extends HasColumn, HasItem, HasValue, RegistryMixinProperties, ThemeableProperties { | ||
export interface CellProperties extends HasColumn, HasContent, HasItem, HasValue, RegistryMixinProperties, ThemeableProperties { |
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.
What is the purpose of HasValue
for Cell
? It's not used in the rendering... is it envisioned that one might want to read the value of value
from a Cell
instance?
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.
That's my thought, yes.
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.
A few initial comments
src/interfaces.d.ts
Outdated
export interface Column<T> { | ||
import { DNode } from '@dojo/widget-core/interfaces'; | ||
|
||
export interface Column<T = any, V = string> { |
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 should have some documentation for exported interfaces, will help explain the intent of the properties and functions.
src/Row.ts
Outdated
} | ||
|
||
let content: DNode; | ||
if (column.render) { |
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'm not sure I get why we need render
and renderValue
, couldn't there just be a render
(perhaps a better name, to not conflate with a widgets render
function) and just have an optional third parameter value?: V
?
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've been going back and forth on this, and it's been discussed in the referenced issue. My intent with render was to have a function that indicates it will not be consuming any data - adding an action button, for example. Absent this, we will still attempt to read a value from the underlying data - which isn't horrible but creates a side-effect that may be unexpected and could (though it would be unlikely) create problems.
Some alternatives I'd been considering:
- Add something like an enum that can be assigned to
field
that would indicate no value should be read from the data object - Allow
get
to be either a function or a value - perhaps rename this property tovalue
What do you think of these alternatives?
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 fully understand the problem with having a single function but with an API that passes the value field, this leaves it up to the consumer to decide how to implement the customisation and choose to use the value or not.
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 most general use-case in our experience is having a custom get
and a custom renderValue
. In this situation, the user will probably only care about the value and not the item or column, so having value first would be ideal. If they don't care about having a value, having value last would be ideal. I'm messing around with this signature and I'm not 100% happy with having value last.
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.
Then do you think it would be more ergonomic to have an options style API?
interface CustomRenderOptions {
item: any;
column: Column;
value: any;
}
// consumer implements function and only uses what they need from the options
function customRender(options: CustomRenderOptions): DNode {
// do something with either options.item, options.column or options.value
// or any combination.
}
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 am still not sure about needing get
to be a function when the existing render
function can be used for that.
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 had considered the options API, but with this covering the most common use cases in the first arguments, it doesn't seem worth the overhead.
Do you have any suggestions for a better name?
render
may be used without get
and get
may be used without render
. Combining everything into a single function will tightly couple the customization our users have traditionally done as well as force users to implement what are pretty standard defaults.
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.
With the options
I'd just expect consumers to destructure what is needed at the top of their custom renderer function (which I think is ergonomic).
const { value } = options;
Both get
and render
return a DNode
and are used to effectively populate content
(i'm not sure why cell needs value
now there is content
) that is used by Cell
? Just feels like having two functions that end up as the same thing - content
is more confusing than flexible (would be for me), because I can return a string either based on item and column.
So just having a API for customCellRenderer(item: any, column: Column): DNode {}
, should cover all the scenarios needed?
const { customCellRenderer, field = column.id } = column;
let content = item.data[ field ] ? String(item.data[ field ]) : '';
if (customCellRenderer) {
content = customCellRenderer(item, column);
}
Means a consumer can return a
- Static string:
function customCellRenderer(item: any, column: Column): DNode {
return 'static-string';
}
- Dynamic string based on the item
function customCellRenderer(item: any, column: Column): DNode {
const { field = column.id } = column;
const value = item[field];
return `Miss ${value}`;
}
- A custom DNode
function customCellRenderer(item: any, column: Column): DNode {
return v('button', { /* some properties */});
}
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.
Unless I've missed a scenario that having the two separate functions covers.
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.
get
is specific to finding the value to be used in a cell (the combination of the item and column) and is meant to present an alternative to the default method of retrieving this value from the item based on a field.
render
is meant to take this value and turn it into the UI that will be used inside the cell. render
is reusable, whereas get
is not. So render
could appear across all columns, while get
is unique to each column.
I made it so that get
could return a DNode
so that if someone really wanted to do everything in a single function, they could (though I agree it isn't great practice, it just seemed the best way to do things if we wanted to avoid having separate render methods for when you do and do not care about the cell's value).
I hope this is enough to explain why get
and render
, value
and content
are two separate ideas that seem to encourage separate levels of customization. If that can be agreed upon, I can remove the DNode
return from get
so it retains this clear separation and I guess I can also make the render
function accept an object, though I wish there was a way to do this without so much overhead.
const { id, field } = column; | ||
const { field, id } = column; | ||
|
||
let value: any; |
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.
Perhaps would be nicer it could be simplified a bit (does assume consolidating the two render functions):
let value = item.data[ field || id] ? String(item.data[ field || id ]) : '';
if (column.get) {
value = column.get(item, column);
}
let content: DNode = value;
if (column.render) {
content = column.render(item, column, value);
}
src/interfaces.d.ts
Outdated
* @property get Manually return the value or content to use for this column | ||
* @property render Use this column's value to provide content for this column | ||
*/ | ||
export interface Column<T = any, V = string> { |
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.
Alphabetise please... :-D
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 think I'm reading the style guide on interface property ordering correctly - listing properties then methods. I couldn't find anything on ordering documentation for interfaces but is that what you're referring to here that needs alphebetized?
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.
yes, we need to make it explicit, we have been trying to alphabetize things by default, as when they get added to, it becomes difficult to find stuff in big interefaces... properties then methods, yup, but alphabetical within those groups
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.
Is it not alphabetized within each group right now?
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.
get
is listed as a property with a value of method (or a string), where render
is an optional method... not the same class of properties
Type: feature
The following has been addressed in the PR:
Description:
Adds new callbacks to the
Column
interface, updatesRow
to use the newColumn
definition to findCell
content, and adds unit tests to check the new behavior.