Skip to content

Commit 1bf0d3a

Browse files
authored
fix dataset not being passed to office-ui-fabric-react in contextual menu items declared in template (directives) (#75)
1 parent ffe3e74 commit 1bf0d3a

File tree

7 files changed

+71
-8
lines changed

7 files changed

+71
-8
lines changed

apps/demo/src/app/app.component.html

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,30 @@ <h2>Getting up and running...</h2>
4141
<fab-pivot-item headerText="Tab 3"> <div>Tab 3's content</div> </fab-pivot-item>
4242
</fab-pivot>
4343

44+
<fab-default-button text="button">
45+
<contextual-menu-item key="item1" text="item1" data-track-type="button > item1"></contextual-menu-item>
46+
<contextual-menu-item key="item2" text="item2" data-track-type="button > item2"></contextual-menu-item>
47+
</fab-default-button>
48+
4449
<fab-command-bar>
4550
<items>
4651
<fab-command-bar-item
4752
key="run"
4853
text="Run"
4954
[iconProps]="{ iconName: 'CaretRight' }"
5055
[disabled]="runDisabled"
56+
data-track-type="run"
5157
></fab-command-bar-item>
5258
<fab-command-bar-item
5359
key="new"
5460
text="New"
5561
[iconProps]="{ iconName: 'Add' }"
5662
(click)="onNewClicked()"
5763
></fab-command-bar-item>
58-
<fab-command-bar-item
59-
key="copy1"
60-
text="Copy1"
61-
[iconProps]="{ iconName: 'Copy' }"
62-
(click)="onCopyClicked()"
63-
></fab-command-bar-item>
64+
<fab-command-bar-item key="copy" text="Copy" [iconProps]="{ iconName: 'Copy' }" (click)="onCopyClicked()">
65+
<contextual-menu-item key="item1" text="Item1" data-track-type="copy1 > item1"></contextual-menu-item>
66+
<contextual-menu-item key="item2" text="Item2" data-track-type="copy1 > item2"></contextual-menu-item>
67+
</fab-command-bar-item>
6468
<fab-command-bar-item
6569
key="copy2"
6670
text="Copy2"

libs/fabric/src/lib/components/button/base-button.component.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { Subscription } from 'rxjs';
2424
import { CommandBarItemChangedPayload } from '../command-bar/directives/command-bar-item.directives';
2525
import { mergeItemChanges } from '../core/declarative/item-changed';
2626
import { omit } from '../../utils/omit';
27+
import { getDataAttributes } from '../../utils/get-data-attributes';
2728

2829
export abstract class FabBaseButtonComponent extends ReactWrapperComponent<IButtonProps>
2930
implements OnInit, AfterContentInit, OnDestroy {
@@ -167,6 +168,7 @@ export abstract class FabBaseButtonComponent extends ReactWrapperComponent<IButt
167168
'ngOnDestroy',
168169
'ngAfterContentInit'
169170
),
171+
...getDataAttributes(directive.elementRef.nativeElement, true),
170172
onClick: (ev, item) => {
171173
directive.click.emit({ ev: ev && ev.nativeEvent, item: item });
172174
},

libs/fabric/src/lib/components/command-bar/directives/command-bar-item.directives.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4-
import { ContentChild, Directive, Input, TemplateRef } from '@angular/core';
4+
import { ContentChild, Directive, Input, TemplateRef, ElementRef } from '@angular/core';
55
import { ContextualMenuItemDirective } from '../../contextual-menu/directives/contextual-menu-item.directive';
66
import { ItemChangedPayload } from '../../core/declarative/item-changed.payload';
77
import {
@@ -37,4 +37,8 @@ export class CommandBarItemDirective extends ContextualMenuItemDirective impleme
3737
@Input() cacheKey?: ICommandBarItemOptions['cacheKey'];
3838
@Input() renderedInOverflow?: ICommandBarItemOptions['renderedInOverflow'];
3939
@Input() commandBarButtonAs?: ICommandBarItemOptions['commandBarButtonAs'];
40+
41+
constructor(elementRef: ElementRef<HTMLElement>) {
42+
super(elementRef);
43+
}
4044
}

libs/fabric/src/lib/components/command-bar/directives/command-bar-items.directives.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ import { ContentChildren, Directive, QueryList } from '@angular/core';
66
import { ChangeableItemsDirective } from '../../core/shared/changeable-items.directive';
77
import { ICommandBarItemOptions } from '../command-bar.component';
88
import { CommandBarItemDirective } from './command-bar-item.directives';
9+
import { getDataAttributes } from '../../../utils/get-data-attributes';
910

1011
export abstract class CommandBarItemsDirectiveBase extends ChangeableItemsDirective<ICommandBarItemOptions> {
1112
abstract readonly directiveItems: QueryList<CommandBarItemDirective>;
1213

1314
get items() {
1415
return (
1516
this.directiveItems &&
16-
this.directiveItems.map<ICommandBarItemOptions>(directiveItem => ({
17+
this.directiveItems.map<ICommandBarItemOptions>((directiveItem: CommandBarItemDirective) => ({
1718
...directiveItem,
19+
...getDataAttributes(directiveItem.elementRef.nativeElement, true),
1820
onClick: (ev, item) => {
1921
directiveItem.click.emit({
2022
ev: ev && ev.nativeEvent,

libs/fabric/src/lib/components/contextual-menu/directives/contextual-menu-item.directive.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
QueryList,
1313
ContentChild,
1414
TemplateRef,
15+
ElementRef,
1516
} from '@angular/core';
1617
import { IContextualMenuItem } from 'office-ui-fabric-react';
1718
import { KnownKeys, InputRendererOptions } from '@angular-react/core';
@@ -20,6 +21,7 @@ import { OnChanges } from '../../../declarations/angular/typed-changes';
2021
import { ItemChangedPayload } from '../../core/declarative/item-changed.payload';
2122
import { ChangeableItemsHelper, IChangeableItemsContainer } from '../../core/shared/changeable-helper';
2223
import { ChangeableItemDirective } from '../../core/shared/changeable-item.directive';
24+
import { getDataAttributes } from '../../../utils/get-data-attributes';
2325

2426
export type ContextualMenuItemChangedPayload = ItemChangedPayload<
2527
IContextualMenuItemOptions['key'],
@@ -101,6 +103,10 @@ export class ContextualMenuItemDirective extends ChangeableItemDirective<IContex
101103
return this._changeableItemsHelper && this._changeableItemsHelper.onItemsChanged;
102104
}
103105

106+
constructor(readonly elementRef: ElementRef<HTMLElement>) {
107+
super();
108+
}
109+
104110
private _changeableItemsHelper: ChangeableItemsHelper<IContextualMenuItem>;
105111

106112
ngAfterContentInit() {
@@ -129,6 +135,7 @@ export class ContextualMenuItemDirective extends ChangeableItemDirective<IContex
129135
private _directiveToContextualMenuItem(directive: ContextualMenuItemDirective): IContextualMenuItem {
130136
return {
131137
...directive,
138+
...getDataAttributes(directive.elementRef.nativeElement, true),
132139
onClick: (ev, item) => {
133140
directive.click.emit({ ev: ev && ev.nativeEvent, item: item });
134141
},
@@ -143,6 +150,11 @@ export interface IContextualMenuItemOptions<TData = any>
143150
readonly renderIcon?: InputRendererOptions<IContextualMenuItemOptionsRenderIconContext>;
144151
readonly render?: InputRendererOptions<IContextualMenuItemOptionsRenderContext>;
145152
readonly data?: TData;
153+
154+
/**
155+
* For any attributes like data-* etc.
156+
*/
157+
[propertyName: string]: any;
146158
}
147159

148160
export interface IContextualMenuItemOptionsRenderContext {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { kebabCase } from './kebab-case';
2+
3+
/**
4+
* Gets the data attributes on an `HTMLElement`.
5+
*
6+
* @example
7+
```keepDataPrefix === false```:
8+
```html
9+
<div data-service="Foo" data-service-type="bar"></div> -> { 'service': 'Foo', 'service-type': 'Bar' }
10+
```
11+
12+
```keepDataPrefix === true```:
13+
```html
14+
<div data-service="Foo" data-service-type="bar"></div> -> { 'data-service': 'Foo', 'data-service-type': 'Bar' }
15+
```
16+
*/
17+
export function getDataAttributes<T extends HTMLElement>(
18+
element: T,
19+
keepDataPrefix: boolean = false
20+
): Record<string, string> {
21+
return Object.entries(element.dataset).reduce(
22+
(acc, [key, value]) => ({
23+
...acc,
24+
[calculateKey(key, keepDataPrefix)]: value,
25+
}),
26+
{}
27+
);
28+
}
29+
30+
function calculateKey(key: string, keepDataPrefix: boolean): string {
31+
return `${keepDataPrefix && 'data-'}${kebabCase(key)}`;
32+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/** Implementation borrowed from https://github.com/joakimbeng/kebab-case */
2+
3+
const KEBAB_REGEX = /[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g;
4+
5+
export function kebabCase(str: string) {
6+
return str.replace(KEBAB_REGEX, match => '-' + match.toLowerCase());
7+
}

0 commit comments

Comments
 (0)