Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/accordionpane/tests/functional/AccordionPane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ registerSuite('AccordionPane', {
.then((size: { height: number }) => {
assert.isBelow(size.height, 50);
})
.findByCssSelector('[role="heading"]')
.findByCssSelector('button')
.click()
.end()
.sleep(DELAY)
Expand Down
37 changes: 27 additions & 10 deletions src/button/Button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,30 @@ export default class Button extends ButtonBase<ButtonProperties> {
private _onTouchEnd (event: TouchEvent) { this.properties.onTouchEnd && this.properties.onTouchEnd(event); }
private _onTouchCancel (event: TouchEvent) { this.properties.onTouchCancel && this.properties.onTouchCancel(event); }

protected getContent(): DNode[] {
return this.children;
}

protected getModifierClasses(): (string | null)[] {
const {
disabled,
popup = false,
pressed
} = this.properties;

return [
disabled ? css.disabled : null,
popup ? css.popup : null,
pressed ? css.pressed : null
];
}

protected renderPopupIcon(): DNode {
return v('i', { classes: this.classes(css.addon, iconCss.icon, iconCss.downIcon),
role: 'presentation', 'aria-hidden': 'true'
});
}

render(): DNode {
let {
describedBy,
Expand All @@ -87,12 +111,7 @@ export default class Button extends ButtonBase<ButtonProperties> {
}

return v('button', {
classes: this.classes(
css.root,
disabled ? css.disabled : null,
popup ? css.popup : null,
pressed ? css.pressed : null
),
classes: this.classes(css.root, ...this.getModifierClasses()),
disabled,
id,
name,
Expand All @@ -115,10 +134,8 @@ export default class Button extends ButtonBase<ButtonProperties> {
'aria-pressed': typeof pressed === 'boolean' ? pressed.toString() : null,
'aria-describedby': describedBy
}, [
...this.children,
popup ? v('i', { classes: this.classes(css.addon, iconCss.icon, iconCss.downIcon),
role: 'presentation', 'aria-hidden': 'true'
}) : null
...this.getContent(),
popup ? this.renderPopupIcon() : null
]);
}
}
3 changes: 3 additions & 0 deletions src/button/tests/functional/Button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ function getPage(remote: Remote) {
.setFindTimeout(5000);
}

const DELAY = 750;

registerSuite('Button', {
'button should be visible'() {
return getPage(this.remote)
Expand Down Expand Up @@ -49,6 +51,7 @@ registerSuite('Button', {
assert.isNull(pressed, 'Initial state should be null');
})
.click()
.sleep(DELAY)
.end()
.findByCssSelector(`#example-4 .${css.root}`)
.getAttribute('aria-pressed')
Expand Down
127 changes: 71 additions & 56 deletions src/calendar/Calendar.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { WidgetBase } from '@dojo/widget-core/WidgetBase';
import { ThemeableMixin, ThemeableProperties, theme } from '@dojo/widget-core/mixins/Themeable';
import { v, w } from '@dojo/widget-core/d';
import { DNode, Constructor } from '@dojo/widget-core/interfaces';
import { DNode } from '@dojo/widget-core/interfaces';
import uuid from '@dojo/core/uuid';
import { Keys } from '../common/util';
import { CalendarMessages } from './DatePicker';
import DatePicker from './DatePicker';
import DatePicker, { Paging } from './DatePicker';
import CalendarCell from './CalendarCell';
import * as css from './styles/calendar.m.css';
import * as baseCss from '../common/styles/base.m.css';
Expand All @@ -16,7 +16,6 @@ import * as iconCss from '../common/styles/icons.m.css';
*
* Properties that can be set on a Calendar component
*
* @property CustomDateCell Custom widget constructor for the date cell. Should use CalendarCell as a base.
Copy link
Member

Choose a reason for hiding this comment

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

Did you remove sub-widgets as part of this PR as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The problem is sub-widgets make it much easier to handle data binding on event handlers (e.g. onClick on a specific date cell returns the correct date and disabled state). Based on talking with Ant, I think the compromise reached was to leave in sub-widgets, but try not to expose them to the user

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've been wondering if it makes sense to remove the typedoc for CalendarCell. It's marginally useful to developers wanting to extend CalendarCell, but I definitely don't want it showing up in Dojo documentation

* @property labels Customize or internationalize accessible text for the Calendar widget
* @property month Set the currently displayed month, 0-based
* @property monthNames Customize or internationalize full month names and abbreviations
Expand All @@ -30,7 +29,6 @@ import * as iconCss from '../common/styles/icons.m.css';
* @property onDateSelect Function called when the user selects a date
*/
export interface CalendarProperties extends ThemeableProperties {
CustomDateCell?: Constructor<CalendarCell>;
labels?: CalendarMessages;
month?: number;
monthNames?: { short: string; long: string; }[];
Expand Down Expand Up @@ -234,7 +232,6 @@ export default class Calendar extends CalendarBase<CalendarProperties> {
month,
year
} = this._getMonthYear();
const { theme = {}, CustomDateCell = CalendarCell } = this.properties;

const currentMonthLength = this._getMonthLength(month, year);
const previousMonthLength = this._getMonthLength(month - 1, year);
Expand Down Expand Up @@ -279,19 +276,9 @@ export default class Calendar extends CalendarBase<CalendarProperties> {
isSelectedDay = false;
}

days.push(w(CustomDateCell, {
key: `date-${week * 7 + i}`,
callFocus: this._callDateFocus && isCurrentMonth && date === this._focusedDay,
date,
disabled: !isCurrentMonth,
focusable: isCurrentMonth && date === this._focusedDay,
selected: isSelectedDay,
theme,
today: isCurrentMonth && dateString === todayString,
onClick: this._onDateClick,
onFocusCalled: this._onDateFocusCalled,
onKeyDown: this._onDateKeyDown
}));
const isToday = isCurrentMonth && dateString === todayString;

days.push(this.renderDateCell(date, week * 7 + i, isSelectedDay, isCurrentMonth, isToday));
}

weeks.push(v('tr', days));
Expand All @@ -300,19 +287,30 @@ export default class Calendar extends CalendarBase<CalendarProperties> {
return weeks;
}

private _renderWeekdayCell(day: { short: string; long: string; }): DNode {
const { renderWeekdayCell } = this.properties;
return renderWeekdayCell ? renderWeekdayCell(day) : v('abbr', { title: day.long }, [ day.short ]);
protected renderDateCell(date: number, index: number, selected: boolean, currentMonth: boolean, today: boolean): DNode {
const { theme = {} } = this.properties;

return w(CalendarCell, {
key: `date-${index}`,
callFocus: this._callDateFocus && currentMonth && date === this._focusedDay,
date,
disabled: !currentMonth,
focusable: currentMonth && date === this._focusedDay,
selected,
theme,
today,
onClick: this._onDateClick,
onFocusCalled: this._onDateFocusCalled,
onKeyDown: this._onDateKeyDown
});
}

protected render(): DNode {
protected renderDatePicker(): DNode {
const {
labels = DEFAULT_LABELS,
monthNames = DEFAULT_MONTHS,
renderMonthLabel,
selectedDate,
theme = {},
weekdayNames = DEFAULT_WEEKDAYS,
onMonthChange,
onYearChange
} = this.properties;
Expand All @@ -321,38 +319,65 @@ export default class Calendar extends CalendarBase<CalendarProperties> {
year
} = this._getMonthYear();

return w(DatePicker, {
key: 'date-picker',
labelId: this._monthLabelId,
labels,
month,
monthNames,
renderMonthLabel,
theme,
year,
onPopupChange: (open: boolean) => {
this._popupOpen = open;
},
onRequestMonthChange: (requestMonth: number) => {
onMonthChange && onMonthChange(requestMonth);
},
onRequestYearChange: (requestYear: number) => {
onYearChange && onYearChange(requestYear);
}
});
}

protected renderPagingButtonContent(type: Paging): DNode[] {
const { labels = DEFAULT_LABELS } = this.properties;
const iconClass = type === Paging.next ? iconCss.rightIcon : iconCss.leftIcon;
const labelText = type === Paging.next ? labels.nextMonth : labels.previousMonth;

return [
v('i', { classes: this.classes(iconCss.icon, iconClass),
role: 'presentation', 'aria-hidden': 'true'
}),
v('span', { classes: this.classes().fixed(baseCss.visuallyHidden) }, [ labelText ])
];
}

protected renderWeekdayCell(day: { short: string; long: string; }): DNode {
const { renderWeekdayCell } = this.properties;
return renderWeekdayCell ? renderWeekdayCell(day) : v('abbr', { title: day.long }, [ day.short ]);
}

protected render(): DNode {
const {
selectedDate,
weekdayNames = DEFAULT_WEEKDAYS
} = this.properties;

// Calendar Weekday array
const weekdays = [];
for (const weekday in weekdayNames) {
weekdays.push(v('th', {
role: 'columnheader',
classes: this.classes(css.weekday)
}, [
this._renderWeekdayCell(weekdayNames[weekday])
this.renderWeekdayCell(weekdayNames[weekday])
]));
}

return v('div', { classes: this.classes(css.root) }, [
// header
w(DatePicker, {
key: 'date-picker',
labelId: this._monthLabelId,
labels,
month,
monthNames,
renderMonthLabel,
theme,
year,
onPopupChange: (open: boolean) => {
this._popupOpen = open;
},
onRequestMonthChange: (requestMonth: number) => {
onMonthChange && onMonthChange(requestMonth);
},
onRequestYearChange: (requestYear: number) => {
onYearChange && onYearChange(requestYear);
}
}),
this.renderDatePicker(),
// date table
v('table', {
cellspacing: '0',
Expand All @@ -374,22 +399,12 @@ export default class Calendar extends CalendarBase<CalendarProperties> {
classes: this.classes(css.previous),
tabIndex: this._popupOpen ? -1 : 0,
onclick: this._onMonthPageDown
}, [
v('i', { classes: this.classes(iconCss.icon, iconCss.leftIcon),
role: 'presentation', 'aria-hidden': 'true'
}),
v('span', { classes: this.classes().fixed(baseCss.visuallyHidden) }, [ labels.previousMonth ])
]),
}, this.renderPagingButtonContent(Paging.previous)),
v('button', {
classes: this.classes(css.next),
tabIndex: this._popupOpen ? -1 : 0,
onclick: this._onMonthPageUp
}, [
v('i', { classes: this.classes(iconCss.icon, iconCss.rightIcon),
role: 'presentation', 'aria-hidden': 'true'
}),
v('span', { classes: this.classes().fixed(baseCss.visuallyHidden) }, [ labels.nextMonth ])
])
}, this.renderPagingButtonContent(Paging.next))
])
]);
}
Expand Down
17 changes: 11 additions & 6 deletions src/calendar/CalendarCell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,28 +65,33 @@ export default class CalendarCell extends CalendarCellBase<CalendarCellPropertie
return v('span', [ `${date}` ]);
}

protected render(): DNode {
protected getModifierClasses(): (string | null)[] {
const {
date,
disabled = false,
focusable = false,
selected = false,
today = false
} = this.properties;

const dateCellClasses = [
css.date,
return [
disabled ? css.inactiveDate : null,
selected ? css.selectedDate : null,
today ? css.todayDate : null
];
}

protected render(): DNode {
const {
date,
focusable = false,
selected = false
} = this.properties;

return v('td', {
key: 'root',
role: 'gridcell',
'aria-selected': `${selected}`,
tabIndex: focusable ? 0 : -1,
classes: this.classes(...dateCellClasses),
classes: this.classes(css.date, ...this.getModifierClasses()),
onclick: this._onClick,
onkeydown: this._onKeyDown
}, [ this.formatDate(date) ]);
Expand Down
Loading